【问题标题】:fsync() and write() in different threadsfsync() 和 write() 在不同的线程中
【发布时间】:2014-03-31 11:10:50
【问题描述】:

我正在尝试使用 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 thread\n";
       close (fd);
       return -1;
    }
    while (1) {
       if (write (fd, 'x', 1) < 1)
          std::cout << "ERROR write()\n";
    }
    close(fd);
}

问题是:

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

【问题讨论】:

    标签: c++ c linux io


    【解决方案1】:

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

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

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


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

    【讨论】:

    • FWIW,我不同意你不应该 fsync;如果有什么 fsync() 在 close() 之前应该是关心保留数据的应用程序的例行程序。另外,请参阅sync_file_range 的手册,其中(现在)警告“此系统调用非常危险,不应在可移植程序中使用。” (然后继续解释)。
    • @CraigRinger:fsync-before-close 有效地禁用了延迟写回,这是默认设置,对于 99.9% 的应用程序来说已经足够了。假设硬件没有遭受灾难性故障,也没有意外断电(UPS?),惰性写回是 100% 可靠的。但是,如果发生灾难性故障,无论如何你都不走运。如果硬盘驱动器在同步中爆炸,我该怎么办?至于sync_file_range,我同意,我的回答也反映了这一点。但是,OP 不能等待fsync,遗憾的是,这是你能得到的最好的东西。
    【解决方案2】:

    除非您需要线程用于其他用途,否则我建议您使用异步 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);
    

    【讨论】:

      猜你喜欢
      • 2021-04-25
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 2014-06-20
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 2015-07-23
      相关资源
      最近更新 更多