我有一个作为大容量存储设备的 USB 设备,dmesg 如下所示:
[ 4.416584] scsi 0:0:0:0: Direct-Access Adap ECU Modular ECU 1.0 PQ: 0 ANSI: 2
[ 4.420186] sd 0:0:0:0: [sda] 131072 512-byte logical blocks: (67.1 MB/64.0 MiB)
[ 4.421063] sd 0:0:0:0: [sda] Write Protect is off
[ 4.421084] sd 0:0:0:0: [sda] Mode Sense: 03 00 00 00
[ 4.422053] sd 0:0:0:0: [sda] No Caching mode page found
[ 4.422067] sd 0:0:0:0: [sda] Assuming drive cache: write through
[ 7.446823] sd 0:0:0:0: Attached scsi generic sg0 type 0
它不包含文件系统,也不应该包含文件系统。您无需安装它,只需打开并读取(在本例中)/dev/sda
问题是当我打开并读取设备时,我会第一次得到我想要的东西,然后每次后续读取我都会得到(我假设是)缓存的数据。我做出这个假设是因为设备上的 IO 指示灯在后续读取后不会闪烁,仅在第一次读取时才会闪烁,当然因为 myfread()
包含与第一次读取完全相同的数据fread()
。
我的代码很好,因为我在 Windows 上编译了它并指定了 \.\X: 作为要打开的文件,并且它能够在每次调用时使用新数据很好地轮询数据。
所以我假设 Linux 正在缓存/缓冲读取。
我的代码如下所示:
#define STARTBYTE 272384
#define ENDBYTE 274432
#define NUM_VARS 1024
int main() {
FILE *input = fopen("/dev/sda", "r+");
setbuf(input, NULL);
int exit = 0;
while (exit < 1) {
signed short liveBuffer[NUM_VARS];
fseek(input, STARTBYTE, SEEK_SET);
fread(liveBuffer, 2, NUM_VARS, input);
fflush(input);
// do stuff with the now filled liveBuffer data
}
fclose(input);
return 0;
}
我也尝试过使用open()
和 指定O_DIRECT
,没有区别。
目的是,我打开设备(/dev/sda),寻求272384字节。读取2048字节。重新寻找相同位置,读取 2048 字节等。
如果我fclose()
重新保存该文件open()
,我就会得到新的数据。除非它很慢。大约 10 个样本/秒,在 Windows 上(没有打开/关闭)时,我得到大约 50 个/秒。
我注意到的一个想法是,当我让编译的代码运行几秒钟然后终止它(CTRL+C)时,我会看到设备上的活动灯在我让代码运行的持续时间内疯狂闪烁。
有人能指出我正确的方向吗?
我已经这样想了好几天了,我想我快要上吊了。
答案1
你使用干预措施使问题变得有点复杂标准输入输出层,并忽略您正在调用的函数的返回码。您可以用来O_DIRECT
从设备获取新数据,但您必须遵守由此规定的义务。
特别是,寻道偏移、缓冲区地址和 I/O 大小都必须是 4096 的倍数(或其他 2 的幂,具体取决于设备)。
如果您修改代码以删除当前的 liveBuffer 声明并在开头包含以下代码,您应该会发现它可以工作。
#define PAGE 4096
#define STARTBYTE (272384/PAGE*PAGE) // must align
#define OFFSET (272384-STARTBYTE)
#define ITEMSIZE (sizeof(*liveBuffer))
#define LIVEBUFSIZE ((OFFSET+NUM_VARS*ITEMSIZE+PAGE-1)/PAGE*PAGE)
signed short *liveBuffer;
if(posix_memalign((void**)&liveBuffer, PAGE, LIVEBUFSIZE)!=0)
exit(5);
if (fcntl(fileno(input), F_SETFL, O_DIRECT) == -1)
exit(6);
您现在必须fread()
使用LIVEBUFSIZE/ITEMSIZE
而不是 NUM_VARS。由于STARTBYTE
必须对齐,您现在需要进一步进入数组liveBuffer
以OFFSET/ITEMSIZE
查找所需的数据。您还应该更改所有调用以检查返回代码是否正确。