我无法在使用4.2.0-42-generic内核的Ubuntu 14.04.4 LTS x86_64上复制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;
}
使用以下方式进行编译:
gcc -Wall -O2 block-flush.c -o block-flush
以root权限运行,并在命令行指定分区或块设备进行操作:
sudo ./block-flush /dev/sda3
对于未挂载的分区,以及磁盘(
/dev/sdx
)本身,此命令会输出
/dev/sdxN: Flushed.
。我使用一个外部USB SATA托架和一个“响亮”的3.5英寸硬盘进行了测试(这种托架需要外部电源;USB电源对于这些带有旋转碟片的较大驱动器不足)。我可以清楚地听到ioctl()确实访问了物理设备,因此它不是无操作(而且,在我的测试中,最小化的测试程序从未报告任何错误)。在关闭描述符后,直到打开磁盘或分区进行进一步访问之前,磁盘也处于静止状态。当然,这些观察结果只适用于USB连接的硬盘,仅适用于特定的内核和硬件架构,但在我看来,这表明
ioctl(descriptor, BLKFLSBUF);
应该按预期方式适用于未挂载的分区和完整的块设备。
fsync(_fd)
和fdatasync(_fd)
应该会将内容刷新到底层设备,即使_fd
指向块设备。实际的刷新是由[fs/block_dev.c:blkdev_fsync()] (https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/block_dev.c#n368)(通过[fs/sync.c:fdatasync()] (https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/sync.c#n230) → do_fsync() → vfs_fsync() → vfs_fsync_range(),然后通过file_operations结构的blkdev_fsync())完成的。 - Nominal Animal