我正在尝试使用 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/