在 Linux 中禁用 USB 大容量存储设备的读取缓存/缓冲区

在 Linux 中禁用 USB 大容量存储设备的读取缓存/缓冲区

我有一个作为大容量存储设备的 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必须对齐,您现在需要进一步进入数组liveBufferOFFSET/ITEMSIZE查找所需的数据。您还应该更改所有调用以检查返回代码是否正确。

相关内容