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

标签 c linux flush ioctl

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

所以..这就是我正在尝试做的 - 获取 errno 25。

/dev/sda3 是 ssd 驱动器上的原始未挂载分区

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

ioctl(_fd, BLKFLSBUF, 0)   <== errno = 25. 

Ubuntu 14.04,c

注意:

hdparm -W 0 /dev/sda3

失败:设备的 ioctl 不合适。

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

最佳答案

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

我测试了完整的 block 设备和它们的单独分区。你能试试下面的最小测试程序吗?

将以下内容保存为例如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;
}

编译使用

gcc -Wall -O2 block-flush.c -o block-flush

并运行它(以 root 用户身份),在命令行指定分区或 block 设备:

sudo ./block-flush /dev/sda3

对我来说,这输出 /dev/sdxN: Flushed. 用于未挂载的分区,以及磁盘 (/dev/sdx) 本身。 (另外,在 ioctl() 之前添加 fdatasync(descriptor) 不会改变任何东西,而且它也成功了,没有任何错误。)

此外,我碰巧使用外部 USB SATA 扩展坞和“响亮”的 3.5 英寸驱动器(此类扩展坞需要外部电源;USB 电源不足以满足这些带有旋转盘片的较大驱动器)进行测试。我很容易听到ioctl() 确实访问了物理设备,因此它不是空操作(而且,最小测试程序在我的测试中从未报告过任何失败)。关闭描述符后,磁盘也处于静止状态,直到磁盘或分区被打开以供进一步访问。当然,这些观察结果仅适用于 USB 连接的硬盘驱动器,并且仅适用于此特定内核和硬件架构,但在我看来,它确实表明 ioctl(descriptor, BLKFLSBUF); 应该以预期的方式适用于未挂载的分区和完整的 block 设备。

关于c - 如何在 Linux 中使用 ioctl(原始分区)正确刷新磁盘缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38301731/

相关文章:

c - 如何让程序在询问 y 或 n 时重复

c - 发送和接收文件 - 套接字 - c - windows

c - C 问题中的 pow() 函数

c++ - 在 Linux 上监视 C++ 中的文件更改

c - 在C中过滤标点符号的最快方法

linux - nginx 无需 sudo 运行或自动输入密码不需要我输入

python - 使用 python 记录文件和进程超时的输出

c++ - 不能 cin.ignore 直到 EOF?

c++ - std::endl 使 Windows 8 崩溃,使用 MinGW 编译

java - 不满意链接错误 : no libhello in java. library.path