标准如何指定原子写入常规文件(不是管道或fifo)?

时间:2012-08-24 14:15:23

标签: c linux posix systems-programming

posix标准指定当写入少于PIPE_BUF字节到管道或FIFO被授予原子时,也就是说,我们的写入不与其他进程混合'。但是我没有找到关于常规文件的标准规范。我的意思是,当我们写的比PIPE_BUF少时,它也会被授予原子性。但我想知道常规文件有这样的限制吗?我的意思是,管道具有容量,因此当写入管道并超出其容量时,内核将使写入器处于休眠状态,因此其他进程将有机会写入,但是常规文件似乎没有必要有这样的限制,我是对的吗?

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

5 个答案:

答案 0 :(得分:4)

引自http://pubs.opengroup.org/onlinepubs/9699919799/toc.htm(Single UNIX Specification,Version 4,2010 Edition):

  

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

该规范确实解决了在多个读取器的情况下写入有关写入的语义的方式,但正如您从上面所看到的,规范未定义多个并发写入器的行为。

上面提到的关于文件的说明。对于管道和FIFO,PIPE_MAX语义适用,保证并发写入在PIPE_MAX字节之前是不可分割的。

  

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

     

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

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

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

BTW,O_APPEND保证不同进程的所有写操作都会到达文件的末尾。但是,如上所述SUS,如果写入实际上是并发的(同时发生),则行为未定义。在早期的单处理和非先发制人的UNIX上,这并不重要,因为在其他人有机会写作之前完成了写(2)的调用...

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

答案 1 :(得分:2)

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

答案 2 :(得分:0)

文件的几个编写者可能会混淆一些东西。但是,使用O_APPEND打开的文件将按写入访问原则附加。

如果要保留C stdio接口而不是较低级别的接口,请使用“a”或“a +”(映射到O_APPEND)来打开文件,设置足够大的缓冲区,以便不需要写入记录内部并使用fsync强制写入构建它们。我不确定它是否由POSIX保证(C没有说明这一点)。

答案 3 :(得分:0)

对所有原子性问题都有最终解决方案;一个互斥体。将您的写入包含在互斥锁中的日志文件中,并且所有操作都将以原子方式完成。

更简单的解决方案可能是使用Google的GLOG库。一个梦幻般的日志记录系统,远比我梦寐以求的任何东西都好,免费,非GPL和原子。

答案 4 :(得分:0)

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

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

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