Unix对单个文件的读写是否原子化序列化?

21

我想知道对于单个文件的写入是否是原子性的,即write("bla bla")和之后对同一文件的write("herp derp")永远不会交织在一起,例如"bla herp bla derp"。假设这些写入发生在不同的进程或线程中,是什么决定了哪一个会先执行?

另外,read()函数是否总是返回反映文件处于所有以前写入完全完成状态的数据(无论该数据是否实际写入磁盘)?例如,在write("herp derp")之后,所有随后的读取是否始终反映写入文件的全部数据,或者是否有时后续的读取只反映"herp"而不是"derp"(或者有时根本不反映任何数据)?如果读取和写入发生在不同的进程/线程中会发生什么?

我不关心并发文件访问策略。我只想知道read()和write()做了什么。


你的问题很有趣,正是我想问的。哈哈... - Anthony
OP发布了一个后续问题每个Unix文件描述符都有自己的读/写缓冲区吗? - Piotr Dobrogost
你可能会对Linux内核的 线程 感兴趣,该线程的标题是 Update of file offset on write() etc. is non-atomic with I/O ,这导致了此提交 - http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9c225f2655e36a470c4f58dbbc99244c5fc7f2d4 - Piotr Dobrogost
1个回答

11

write()函数的多次调用将被逐个处理,而不是作为单个原子写事务处理。当多个进程/线程向同一文件中写入数据时,它们之间可能会交错执行。实际写操作的顺序由调度程序(包括内核进程调度程序以及“green”线程库的调度程序)确定。

除非另有规定(例如支持的情况下使用O_DIRECT open标志或类似方式),read()write()操作都是在内核缓冲区上进行的。read()函数将优先使用已加载到内存中的缓冲区,而不是再次从磁盘读取。

请注意,这可能会受到本地文件缓存的影响。例如,stdioiostreams将按块将文件数据读入到进程中的缓冲区中,这与内核缓冲区无关。因此,如果对已经缓存在stdio中的数据进行write()操作,则不会看到这些数据的变化。同样,在输出缓冲区存在的情况下,只有在刷新输出缓冲区后才会有任何实际的内核级输出,刷新可以自动发生,因为它已经填满,也可以手动通过使用fflush()或C ++的endl(它会隐式刷新输出缓冲区)。


谢谢,你听起来好像知道你在说什么 :) 我的印象是stdio的读/写是sys_write/sys_read的直接包装器?这不是这种情况吗?此外,在Linux文档中,“POSIX要求可以证明发生在write()返回之后的read(2)返回新数据。”是什么意思? - Jegschemesch
在内核中,单独的文件描述符是否使用单独的读/写缓冲区?如果我在一个进程中读取,它是否可能反映另一个进程从写缓冲区写入的数据?或者无论有多少进程/线程打开它,是否总是一个内核缓冲区每个文件?(顺便说一下,我使用“描述”来指代打开文件的基础表示,而不是作为进程中使用的“描述符”号码的句柄,例如dup()返回现有描述的新描述符) - Jegschemesch
1
这取决于内核的详细信息。在大多数现代类Unix系统中,文件块是全局缓冲的;也就是说,无论有多少进程在给定文件上打开了多少文件描述符,缓冲都是通过块地址进行的,并在它们之间共享。例外情况是O_DIRECT,正如我之前提到的,或者访问原始设备而不是块设备(ls -l分别显示cb,而不是文件的-或目录的d)。 - geekosaur
2
这个提交中的提交消息 - http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9c225f2655e36a470c4f58dbbc99244c5fc7f2d4 - 暗示了 write() 调用是原子性的,不是吗? - Piotr Dobrogost
@PiotrDobrogost 是的。这个错误修复也在 write(2)BUGS 部分有记录(我已经验证了他们提到的版本 3.14 匹配,所以它肯定是关于那个提交的)。 - tne
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接