【问题标题】:Calling fsync(2) after close(2)close(2) 后调用 fsync(2)
【发布时间】:2016-09-14 06:45:18
【问题描述】:

场景:

任务代码(省略错误检查):

// open, write and close
fd = open(name);
write(fd, buf, len);
close(fd);
< more code here **not** issuing read/writes to name but maybe open()ing it >
// open again and fsync
fd = open(name);
fsync(fd);

系统中不再有任务同时访问name

它是否已定义,更重要的是,它是否会在name 引用的 inode 上同步可能的未完成写入?即,我会在 fsync 之后从文件中读回 buf 吗?

来自 POSIX http://pubs.opengroup.org/onlinepubs/009695399/functions/fsync.html 我会说这似乎是合法的......

谢谢。

5 月 18 日编辑: 感谢您的回答和研究。我(在 2016 年)向 extfs 首席开发人员之一(Ted)提出了这个问题并得到了这个答案:“Posix 不保证,但实际上它应该适用于大多数 文件系统,包括 ext4。 Posix 规范中的关键词是:

fsync() 函数应请求打开文件的所有数据 ^^^^^^^^^^^^^^^^^ 由 fildes 命名的描述符将被传输到存储设备 ^^^^^^^^^^^^^^^^^^^^^^^^^^ 与 fildes 描述的文件相关联。

它没有说“fildes描述的文件的所有数据......”它 说“打开文件描述符的所有数据”。所以技术数据 不保证由另一个文件描述符写入 磁盘。

实际上,文件系统不会尝试从哪个 fd 进来的脏数据 开,所以你不必担心。还有一个写得比什么多的操作系统 严格要求是符合标准的,所以这就是你 一般情况下都会找到,即使它没有保证。”这不如“完全相同的持久保证”那么具体,但相当权威,尽管可能已经过时了。

我试图做的是一个适用于单个文件的“同步”命令。 就像 fsync /some/file 一样,无需同步整个文件系统,例如在 shell 脚本中使用它。 现在(从几年前开始)gnu coreutils 'sync' 可以在单个文件上工作,并且正是这样做的(open/fsync)。提交:https://github.com/coreutils/coreutils/commit/8b2bf5295f353016d4f5e6a2317d55b6a8e7fd00

【问题讨论】:

  • 由于你刚刚打开文件描述符,所以没有“filedes命名的打开文件描述符的数据要被传输”,所以我会说没有。
  • @marcolz 这是我的疑问。但我会说 filedes 引用的 inode 上的待处理请求是“打开文件描述符的数据”
  • This comment 建议你可以写“一堆文件不用fsync就可以全部写出来,然后回去重新打开/fsync”,建议fsync之后关闭工程。这样是有意义的——否则在 close() 之后将无法强制将数据写入磁盘。但是如果能得到一个权威的答案就太好了。我已经开始赏金了。
  • 我在linux-fsdevel 上问过这个问题。我got confirmed 至少在撰写本文时,close()/re-open()/fsync() 确实提供与fsync()/close() 相同的保证。

标签: c linux unix filesystems posix


【解决方案1】:

close()+re-open()+fsync() 不提供与fsync()+close() 相同的保证。

来源:我took this questionlinux-fsdevel 邮件列表和got the answer

close()/re-open()/fsync() 序列是否提供相同的持久性 保证为 fsync()/close()?

简短的回答是否定的,后者提供了更好的保证。 更长的答案是持久性保证取决于内核版本, 因为情况在 v4.13、v4.14 和现在再次发生变化 v4.17-rc 和稳定的内核。

更多相关链接是:

特别是后面的链接描述了如何

  • 关闭 FD 后,您将失去所有强制持久性的方法
  • fsync() 失败后,您不能再次调用fsync(),希望现在您的数据会被写入
  • must re-do/confirm all writing work如果发生这种情况

【讨论】:

  • 感谢您的回答。我用一些信息更新了我的问题。
【解决方案2】:

POSIX 的当前 (2017) 规范fsync() 识别基本功能和可选功能:

fsync() 函数应请求将由fildes 命名的打开文件描述符的所有数据传输到与fildes 描述的文件关联的存储设备。传输的性质是实现定义的。在系统完成该操作或检测到错误之前,fsync() 函数不应返回。

[SIO] ⌦ 如果定义了_POSIX_SYNCHRONIZED_IOfsync() 函数将强制与文件描述符fildes 指示的文件关联的所有当前排队的 I/O 操作进入同步 I/O 完成状态。所有 I/O 操作都应按照同步 I/O 文件完整性完成的定义完成。 ⌫

如果实现未定义_POSIX_SYNCHRONIZED_IO,则重新打开的文件描述符没有要传输到存储设备的未写入数据,因此fsync() 调用实际上是一个空操作。

如果_POSIX_SYNCHRONIZED_IO由实现定义,那么您重新打开的文件描述符将确保写入与要传输到存储设备的文件关联的任何文件描述符上的所有数据。

Conformance 上的标准部分包含有关选项和选项组的信息。 Definitions 部分定义了382..387,它定义了同步 I/O 和同步 I/O 的各个方面(是的,它们是不同的——也要注意打开文件描述符和打开文件描述)。 关于同步 I/O 的含义,Realtime 部分参考了定义部分。

它定义:

3.382 同步输入输出

一种确定性和稳健性改进机制,用于增强数据输入和输出机制,以便应用程序可以确保正在处理的数据物理存在于辅助大容量存储设备上。

3.383 同步 I/O 完成

已成功传输或诊断为不成功的 I/O 操作的状态。

3.384 同步 I/O 数据完整性完成

对于读取,当操作已完成或不成功时诊断。仅当数据的图像已成功传输到请求进程时,读取才完成。如果在请求同步读取操作时有任何挂起的写入请求影响要读取的数据,则这些写入请求会在读取数据之前成功传输。

对于写入,当操作已完成或不成功时诊断。只有当写入请求中指定的数据传输成功并且检索数据所需的所有文件系统信息都传输成功时,写入才完成。

数据检索不需要的文件属性(访问时间、修改时间、状态更改时间)不需要在返回调用进程之前成功传输。

3.385 同步 I/O 文件完整性完成

与同步 I/O 数据完整性完成相同,除了与 I/O 操作相关的所有文件属性(包括访问时间、修改时间、状态更改时间)在返回调用进程之前成功传输。

3.386 同步 I/O 操作

对文件执行的 I/O 操作为应用程序提供对其数据和文件完整性的保证。

3.387 同步 I/O 操作

导致请求 I/O 的线程在该 I/O 操作完成之前被阻止进一步使用处理器的 I/O 操作。

注意: 同步 I/O 操作并不意味着同步 I/O 数据完整性完成或同步 I/O 文件完整性完成。

目前尚不清楚“与 [the] 文件描述符指示的文件关联的所有当前排队的 I/O 操作”是否适用于进程。 从概念上讲,我认为应该,但措辞不是黑白的(或浅黄色上的黑色)。它当然应该适用于当前进程中引用同一文件的任何打开的文件描述符。目前尚不清楚它是否会应用于当前进程中先前打开(和关闭)的文件描述符。如果它适用于所有进程,那么它应该包括来自当前进程的排队 I/O。如果它不适用于所有进程,则可能不适用。

鉴于这一点和fsync() 的基本原理说明,到目前为止,假设fsync() 操作对与已关闭文件描述符关联的排队操作没有影响是迄今为止最安全的。如果您希望fsync() 生效,请在关闭文件描述符之前调用它。

【讨论】:

    猜你喜欢
    • 2013-02-27
    • 1970-01-01
    • 2020-09-14
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 2018-04-24
    • 1970-01-01
    • 2017-06-04
    相关资源
    最近更新 更多