11

posix 标准规定,当向管道或 FIFO 写入少于 PIPE_BUF 字节时,授予原子性,也就是说,我们的写入不会与其他进程混合。但我未能找出标准如何指定常规文件。我的意思是,当我们写入少于 PIPE_BUF 时,它也将被授予原子性。但我想知道普通文件有这样的限制吗?我的意思是,管道有容量,所以当写入管道并超出其容量时,内核将使写入器休眠,因此其他进程将有机会写入,但普通文件似乎不必有这样的限制,我说的对吗?

我正在做的是几个进程将日志生成到文件中。当然,设置了 O_APPEND。

4

5 回答 5

5

引自http://pubs.opengroup.org/onlinepubs/9699919799/toc.htm(单一 UNIX 规范,第 4 版,2010 版):

本卷 POSIX.1-2008 未指定从多个进程并发写入文件的行为。应用程序应该使用某种形式的并发控制。

规范确实解决了在多个读取器的情况下写入的语义如何发生,但正如您从上面看到的那样,规范没有定义多个并发写入器的行为。

注意上面谈到文件。对于管道和 FIFO,应用 PIPE_MAX 语义,保证并发写入在 PIPE_MAX 字节内是不可分割的。

对管道或 FIFO 的写入请求应以与常规文件相同的方式处理,但以下例外:

{PIPE_BUF} 字节或更少的写入请求不应与来自其他进程在同一管道上进行写入的数据交错。无论文件状态标志的 O_NONBLOCK 标志是否设置,大于 {PIPE_BUF} 字节的写入可能会在任意边界上与其他进程的写入交错数据。

对于真实的文件系统,情况很复杂。一些本地文件系统可以通过在写入期间锁定文件句柄来强制执行原子写入到任意大小(内存限制),有些可能不会(我试图查看 ext4 逻辑,但在http://lxr.linux.no附近的某处丢失了轨道/linux+v3.5.3/fs/jbd2/transaction.c#L147)。

对于非本地文件系统,结果或多或少是可以抓取的。只是不要尝试在没有某种形式的显式锁定的情况下在网络文件系统上进行并发写入(或者您肯定绝对确定您正在使用的网络文件系统的语义)。

顺便说一句,O_APPEND 保证不同进程的所有写入都到文件末尾。但是,正如上面的 SUS 所指出的,如果写入确实是并发的(同时发生),那么行为是未定义的。在早期的单进程和非抢占式 UNIX 上,这并不重要,因为对 write(2) 的调用在其他人有机会编写之前完成......

对于操作系统(Linux?)和文件系统(ext4?)的特定组合,可以肯定地回答这个问题。一个普遍的答案?正如 SUS 所说——“未定义的行为”。

于 2012-09-12T21:43:44.437 回答
2

我认为这对你很有用:“writev() 写入的数据被写成一个单独的块,不会与其他进程的写入输出混合”,所以你可以使用 writev

于 2012-09-12T04:04:33.377 回答
0

所有原子性问题都有终极解决方案;互斥体。将您对日志文件的写入包装在互斥锁中,所有操作都将自动完成。

一个更简单的解决方案可能是使用 Google 的 GLOG 库。一个很棒的日志系统,比我想象的任何东西都要好得多,免费的,非 GPL 和原子的。

于 2012-09-06T21:07:19.533 回答
0

文件的多个编写者可能会混淆。但是使用 O_APPEND 打开的文件会在每次写入访问时自动附加。

如果您想保留 C stdio 接口而不是较低级别的接口,请使用“a”或“a+”(映射到 O_APPEND)打开文件,设置一个足够大的缓冲区,无需在您的内部写入记录并在完成构建后使用 fsync 强制写入。我不确定它是否由 POSIX 保证(C 对此只字未提)。

于 2012-08-24T14:47:57.850 回答
0

安全地交错它们的一种方法是让所有写入者锁定文件、写入和解锁。

可用于锁定的函数是flock()、lockf() 和fcntl()。

请注意,所有写入者都必须锁定(并且他们都应该使用相同的机制来进行锁定),或者一个不费心获得锁的人仍然可以与另一个持有锁的人同时写入。

于 2015-12-17T04:46:11.890 回答