fsync() 和 write() 在不同的线程中

fsync() and write() in different threads

本文关键字:线程 write fsync      更新时间:2023-10-16

我正在尝试使用 fsync() 和 write() 编写程序,但 fsync 需要时间来同步数据,但我没有时间等待。我为 fsync() 又做了一个线程这是我的代码:

#include <thread>
void thread_func(int fd) {
    while (1) {
       if(fsync(fd) != 0)
           std::cout << "ERROR fsync()n";
       usleep(100);
    }
}
int main () {
    int fd = open ("device", O_RDWR | O_NONBLOCK);
    if (fd < 0) {
        std::cout << "ERROR: open()n";
        return -1;
    }
    std::thread *thr = new std::thread (thread_func, fd);
    if (thr == nullptr) {
       std::cout << "Cannot create threadn";
       close (fd);
       return -1;
    }
    while (1) {
       if (write (fd, 'x', 1) < 1)
          std::cout << "ERROR write()n";
    }
    close(fd);
}

问题是:

当我使用文件描述符在主线程以外的其他线程中进行同步时,是否需要锁定不同的线程?当我在没有互斥锁的情况下测试我的程序时,它没有问题。当我阅读 fsync 的 man 描述时,它没有不同的线程。

如果fsync需要时间,甚至有时在很短的时间内阻塞的事实是一个问题,那么你很可能做错了什么。

通常,您根本不想打电话给fsync。这样做是一种严重的反优化,只有在必须确保数据已写出的情况下,才会想要这样做1.但是,在这种情况下,您绝对希望fsync阻止,这不仅按预期工作,而且是必要的。
只有当fsync回来时,你才知道它已经完成了它的任务。您知道操作系统已尽最大努力确保数据已写入,只有这样才能安全地继续。如果你把它卸载到后台线程,你也可以不调用fsync,因为你不知道什么时候可以安全地假设数据已经写入。

如果启动写入是你的主要目标,则可以在 Linux 下使用 sync_file_range(异步运行),然后在一段时间后调用fsync。跟进fsync的原因既是为了确保完成写入,也是为了确保sync_file_range不会更新元数据,因此除非您严格覆盖文件中已分配的数据,否则即使数据在磁盘上,您的写入也可能在崩溃的情况下不可见(我无法想象这是怎么发生的,因为为文件分配更多扇区必然意味着必须修改元数据, 但手册页明确警告可能会发生这种情况)。

<小时 /> 1fsync函数仍然不能(也不能)保证数据位于永久存储上,它可能仍在缓存层次结构中的某个位置,例如控制器或磁盘的写入缓存。

除非您需要线程进行其他操作,否则我建议您使用异步I/O aio库:

struct aiocb fsync_cb = {
      .aio_fildes = fd
    , .aio_sigevent = {
        .sigev_notify = SIGEV_NONE
    }
}
aio_fsync(O_SYNC, &fsync_cb);

write还有一个等效的变体。

struct aiocb write_cb = {
      .aio_fildes = fd
    , .aio_buf = buffer
    , .aio_nbytes = nbytes
    , .aio_offset = offset
    , .aio_sigevent = {
        .sigev_notify = SIGEV_NONE
    }
}
aio_write(&write_cb);

如果您选择没有任何成功的通知,那么您将不得不在某个时候检查/等待完成:

while (aio_error(&write_cb) == EINPROGRESS);