【问题标题】:Are Unix reads and writes to a single file atomically serialized?Unix 对单个文件的读写是原子序列化的吗?
【发布时间】:2011-07-09 05:25:15
【问题描述】:

我想知道对单个文件的写入是否以原子方式完成,这样对同一个文件的 write("bla bla") 和随后的 write("herp derp") 不会导致交错,例如“bla herp bla derp”。假设这些写入发生在不同的进程或线程中,那么什么决定了哪些先完成?

另外,read() 是否总是返回反映文件处于所有先前写入完全完成状态的数据(无论数据是否已实际写入磁盘)?例如,在 write("herp derp") 之后,所有后续读取是否始终反映写入文件的完整数据,或者后续读取有时仅反映“herp”而不反映“derp”(或有时不反映任何数据完全)?如果读写发生在不同的进程/线程中怎么办?

我对并发文件访问策略不感兴趣。我只想知道读写到底是做什么的。

【问题讨论】:

标签: linux file unix


【解决方案1】:

单独的write() 调用是单独处理的,而不是作为单个原子写入事务,当多个进程/线程写入同一个文件时,交错是完全可能的。实际写入的顺序由调度程序(内核进程调度程序和“绿色”线程的线程库调度程序)决定。

除非您另外指定(O_DIRECTopen 标志或类似标志,如果支持),read()write() 对内核缓冲区进行操作,read() 将优先使用加载的缓冲区而不是再次读取磁盘。

请注意,这可能会因本地文件缓冲而变得复杂;例如,stdioiostreams 将按块将文件数据读取到进程中的缓冲区中,该缓冲区独立于内核缓冲区,因此来自其他地方的 write() 到已经在 stdio 中缓冲的数据将不会见过。同样,使用输出缓冲,在输出缓冲区被刷新之前不会有任何实际的内核级输出,无论是自动因为它已填满,还是由于fflush() 或 C++ 的endl(隐式刷新输出缓冲区)而手动.

【讨论】:

  • Thx,你听起来好像你知道你在说什么:) 我的印象是 stdio 的读/写是 sys_write/sys_read 的直接包装器?不是这样吗?此外,在 linux 文档中这意味着什么:“POSIX 要求可以证明在 write() 返回后发生的 read(2) 返回新数据。”
  • 单独的文件描述是否在内核中使用单独的读/写缓冲区?如果我在一个进程中读取,它是否可能反映从另一个进程写入写入缓冲区的数据?或者,无论在多少进程/线程中打开多少描述,每个文件总是有一个内核缓冲区? (顺便说一句,我使用“描述”来指代打开文件的底层表示,而不是在进程中用作句柄的“描述符”编号,例如 dup() 返回现有描述的新描述符)
  • @Jegschemesch 这意味着如果您write 数据,然后紧接着read,它将始终读取新数据。
  • 答案取决于内核的细节。在大多数现代类 Unix 系统中,文件块是在全局基础上缓冲的。也就是说,无论有多少进程在给定文件上打开了多少文件描述符,缓冲都是由块地址完成并在所有进程之间共享。例外是O_DIRECT,正如我之前提到的,或者访问原始设备而不是块设备(ls -l 分别显示cb,而不是- 用于文件或d目录)。
  • 此提交中的提交消息 - git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/… - 表明 write() 调用是原子的,不是吗?
猜你喜欢
  • 2010-09-08
  • 1970-01-01
  • 2016-07-08
  • 1970-01-01
  • 2011-10-23
  • 2011-01-04
  • 2011-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多