如何在linux中使用ioctl(原始分区)正确刷新磁盘缓存

如何在linux中使用ioctl(原始分区)正确刷新磁盘缓存,c,linux,flush,ioctl,C,Linux,Flush,Ioctl,我正在尝试使用ioctl来确保直接写入卷的更改会影响磁盘。 fsync()显然在原始分区中不可用。sync()也是一个可怕的解决方案(为了刷新64MB,我需要整个生命周期来同步等待) 所以。。以下是我正在尝试做的事情——犯25号错误 /dev/sda3是ssd驱动器上未安装的原始分区 open(_fd, "/dev/sda3", ...) pwritev(_fd, ...) ioctl(_fd, BLKFLSBUF, 0) <== errno = 25. 开启失败:设备的ioct

我正在尝试使用ioctl来确保直接写入卷的更改会影响磁盘。 fsync()显然在原始分区中不可用。sync()也是一个可怕的解决方案(为了刷新64MB,我需要整个生命周期来同步等待)

所以。。以下是我正在尝试做的事情——犯25号错误

/dev/sda3是ssd驱动器上未安装的原始分区

open(_fd, "/dev/sda3", ...)
pwritev(_fd, ...)

ioctl(_fd, BLKFLSBUF, 0)   <== errno = 25. 
开启失败:设备的ioctl不正确


如何为我的ssd找到合适的刷新方法?

我无法使用4.2.0-42-generic内核在x86\u 64上复制Ubuntu 14.04.4 LTS中的
ioctl(fd,BLKFLSBUF)
错误

我测试了全块设备和它们上的各个分区。你能试试下面的最小测试程序吗

将以下内容另存为例如block flush.c:

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int arg, descriptor, result;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s BLOCK-DEVICE-OR-PARTITION ...\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    for (arg = 1; arg < argc; arg++) {

        do {
            descriptor = open(argv[arg], O_RDWR);
        } while (descriptor == -1 && errno == EINTR);
        if (descriptor == -1) {
            const int cause = errno;
            fprintf(stderr, "%s: Cannot open device: %s [%d].\n", argv[arg], strerror(cause), cause);
            return EXIT_FAILURE;
        }

        errno = 0;
        result = ioctl(descriptor, BLKFLSBUF);
        if (result && errno) {
            const int cause = errno;
            fprintf(stderr, "%s: Cannot flush device: %s [%d].\n", argv[arg], strerror(cause), cause);
            return EXIT_FAILURE;
        } else
        if (result)
            fprintf(stderr, "%s: Flush returned %d.\n", argv[arg], result);
        else
        if (errno) {
            const int cause = errno;
            fprintf(stderr, "%s: Flush returned zero, but with error: %s [%d]. Ignored.\n", argv[arg], strerror(cause), cause);
        }

        result = close(descriptor);
        if (result == -1) {
            const int cause = errno;
            fprintf(stderr, "%s: Error closing device: %s [%d].\n", argv[arg], strerror(cause), cause);
            return EXIT_FAILURE;
        }

        fprintf(stderr, "%s: Flushed.\n", argv[arg]);
    }

    return EXIT_SUCCESS;
}
并以root用户身份运行它,在命令行中指定分区或块设备:

sudo ./block-flush /dev/sda3
对我来说,这会为未安装的分区以及磁盘(
/dev/sdx
)本身输出
/dev/sdxN:Flushed.
)。(另外,在ioctl()之前添加
fdatasync(descriptor)
不会改变任何内容,它也会成功,不会出现任何错误。)


此外,我碰巧使用一个外部USB SATA扩展底座和一个“响亮的”3.5“驱动器(此类扩展底座需要外部电源;USB电源不足以支持这些带有旋转盘片的大型驱动器)对此进行了测试。我可以很容易地听到ioctl()确实访问了物理设备,因此它不是一个禁止操作的选项(同样,最小测试程序在我的测试中从未报告任何失败)。关闭描述符后,磁盘也会处于静止状态,直到磁盘或分区被打开以供进一步访问。当然,这些观察结果仅适用于USB连接的硬盘驱动器,并且仅适用于此特定的内核和硬件体系结构,但在我看来,它确实表明
ioctl(描述符,BLKFLSBUF)
应该以预期的方式适用于未安装的分区和全块设备。

使用直接I/O或/dev/raw是一个选项吗?什么意思,“fsync()显然在原始分区中不可用”?
fsync(\u fd)
fdatasync(\u fd)
应将内容刷新到底层设备,即使
\u fd
指的是块设备。实际刷新由(通过→ do_fsync()→ vfs_fsync()→ vfs_fsync_range(),然后通过文件操作结构执行blkdev_fsync()@MarkPlotnick-我正在打开/dev/sda3。不是/dev/raw。我使用了另一台运行hdparam-W 0的机器。仍然ioctl失败,errno=25。所以我的问题是-我可以在/dev/sdxN上使用ioctl-BLKFLSHBUF吗?@NominalAnimal-fsync给我提供内存速率,而不是磁盘。它似乎什么都不起作用。我的场景:1)创建未安装的分区2)fd=open(“/dev/sdaxN“,…权限..)3)写入..现在fsync什么都不做,ioctl(fd,BLKFLSBUF)失败。我缺少什么?我想确保数据是以物理方式写入的
gcc -Wall -O2 block-flush.c -o block-flush
sudo ./block-flush /dev/sda3