访问同一文件的多个进程

时间:2012-03-26 22:25:22

标签: c

多个进程是否可以同时访问(写入)同一文件?使用以下代码,它似乎有效,但我有疑虑。

实例中的用例是每次收到电子邮件时都会调用的可执行文件,并将其输出记录到中央文件中。

if (freopen(console_logfile, "a+", stdout) == NULL || freopen(error_logfile, "a+", stderr) == NULL) {
    perror("freopen");
}
printf("Hello World!");

这是在CentOS上运行并编译为C.

2 个答案:

答案 0 :(得分:8)

使用C标准IO设施引入了一层新的复杂性;该文件仅通过write(2)系统调用(或内存映射,但在本例中未使用)进行修改 - C标准IO包装器可能会推迟写入文件一段时间,并且可能无法提交完整一个系统调用中的请求。

write(2)调用本身应该表现良好:

   [...] If the file was
   open(2)ed with O_APPEND, the file offset is first set to the
   end of the file before writing.  The adjustment of the file
   offset and the write operation are performed as an atomic
   step.

   POSIX requires that a read(2) which can be proved to occur
   after a write() has returned returns the new data.  Note that
   not all file systems are POSIX conforming.

因此,您的基础write(2)调用将正常运行。

对于更高级别的C标准IO流,您还需要处理缓冲。 setvbuf(3)函数可用于请求无缓冲输出,行缓冲输出或块缓冲输出。默认行为从流更改为流 - 如果标准输出和标准错误正在写入终端,则默认情况下它们是行缓冲且无缓冲的。否则,块缓冲是默认值。

如果您的数据自然是面向行的,您可能希望手动选择行缓冲,以防止交错数据。如果您的数据面向行,您可能希望使用未缓冲或保留块缓冲,但只要您累积了一个“单位”输出,就会手动刷新数据。

如果您一次写入超过BUFSIZ个字节,则您的写入可能会交错。 setvbuf(3)函数可以帮助防止交错。

谈论性能可能为时过早,但线路缓冲比块缓冲要慢。如果您的记录接近磁盘的速度,您可能希望完全采用另一种方法来确保您的写入不是交错的。

答案 1 :(得分:1)

这个答案不正确。它确实有效:

  

所以竞争条件是:

     
      
  1. 进程1打开它以进行追加,然后
  2.   
  3. 稍后的进程2打开它以进行追加,然后
  4.   
  5. 稍后仍然1写入并关闭,然后
  6.   
  7. 最后2次写入和关闭。
  8.         

    如果“有效”我会留下深刻的印象,因为我不清楚它是什么   工作应该意味着。我认为“工作”意味着所有写入的字节   由两个进程都在日志文件中?我希望他们两个   从相同的字节偏移开始写入,因此将替换其他字节   字节。它一切都会好起来,包括第3步,只会出现   作为第4步的问题,看起来像一个简单的测试:open getchar   ......写得很近。

         

    他们可以同时打开文件是否至关重要?一个   更明显的解决方案,如果写得快,就是打开独占。

要快速检查系统,请尝试:

/* write the first command line argument to a file called foo
 * stackoverflow topic 9880935
 */

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main (int argc, const char * argv[]) {
    if (argc <2) {
        fprintf(stderr, "Error: need some text to write to the file Foo\n");
        exit(1);
    }

    FILE* fp = freopen("foo", "a+", stdout);

    if (fp == NULL) {
        perror("Error failed to open file\n");
        exit(1);
    }

    fprintf(stderr, "Press a key to continue\n");
    (void) getchar();       /* Yes, I really mean to ignore the character */

    if (printf("%s\n", argv[1]) < 0) {
        perror("Error failed to write to file: ");
        exit(1);        
    }

    fclose(fp);

    return 0;
}