【问题标题】:C: multi-processes stdio append modeC:多进程stdio追加模式
【发布时间】:2012-02-16 03:38:44
【问题描述】:

我用 C 写了这段代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void random_seed(){
    struct timeval tim;
    gettimeofday(&tim, NULL);
    double t1=tim.tv_sec+(tim.tv_usec/1000000.0);
    srand (t1);
}

void main(){
    FILE *f;
    int i;
    int size=100;
    char *buf=(char*)malloc(size);

    f = fopen("output.txt", "a");
    setvbuf (f, buf, _IOFBF, size);
    random_seed();

    for(i=0; i<200; i++){
          fprintf(f, "[ xx - %d - 012345678901234567890123456789 - %d]\n", rand()%10, getpid());
          fflush(f);
    }

    fclose(f);
    free(buf);
}

此代码以附加模式打开一个文件并附加 200 次字符串。 我设置了可以包含完整字符串的大小为 100 的 buf。 然后我使用这个 bash 脚本创建了多个运行这段代码的进程:

#!/bin/bash

gcc source.c
rm output.txt

for i in `seq 1 100`;
do
    ./a.out &
done

我希望在输出中字符串永远不会混淆,因为我读到,当打开带有 O_APPEND 标志的文件时,文件偏移量将在每次写入之前设置为文件末尾,并且我正在使用完全缓冲流,但我得到每个进程的第一行是这样混合的:

[ xx - [ xx - 7 - 012345678901234567890123456789 - 22545]

后面几行

2 - 012345678901234567890123456789 - 22589]

看起来写入因调用 rand 函数而中断。

那么...为什么会出现这些线条? 防止这种情况的唯一方法是使用文件锁定...即使我只使用附加模式?

提前致谢!

【问题讨论】:

    标签: c append stdio logfiles multiprocess


    【解决方案1】:

    您需要自己实现某种形式的并发控制,POSIX 不保证来自多个进程的并发写入。您可以获得管道的一些保证,但不能保证从不同进程写入的常规文件。

    引用POSIX write():

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

    (在基本原理部分的末尾。)

    【讨论】:

    • 感谢您的链接!在同一页中写道“如果设置了文件状态标志的 O_APPEND 标志,则文件偏移量应在每次写入之前设置为文件末尾,并且在更改文件偏移量和更改文件偏移量之间不应发生中间文件修改操作写操作。”
    • 我刚刚看到,当我将缓冲区大小设置为大于 128 的值时,它按预期工作......当我使用小于 128 时,看起来 setvbuf 的结果不正确
    • 您应该将其读作intervening file modification operation shall occur between changing the file offset and the write operation 来自该进程。进程中有/可以锁定以防止多个线程同时写入。进程之间没有这样的保证。另一个进程可以在文件偏移更改和写入之间做一些事情。
    • 关于您的第二条评论:它没有按您的预期工作。它似乎在您启动它时起作用。不能保证它适用于任何缓冲区大小。 (但如果你完全禁用缓冲,它应该经常工作。)
    • #include #include int main(void){ int size=128; char* buf=malloc(大小); setvbuf (stdout, buf, _IOFBF, 大小); printf("5 秒过去了吗?\n");睡眠(5); }
    【解决方案2】:

    您以完全缓冲模式打开文件。这意味着输出的每一行首先进入缓冲区,当缓冲区溢出时,无论它是否包含不完整的行,它都会被刷新到文件中。这会导致同时写入同一文件的不同进程的输出块被交错。

    一个简单的解决方法是在行缓冲模式_IOLBF 中打开文件,以便在每个完整的行上刷新缓冲区。只要确保缓冲区大小至少与最长的行一样大,否则最终会写入不完整的行。缓冲区通常由单个write() 系统调用刷新,因此来自不同进程的行不会相互交错。

    虽然不能保证write() 系统调用对于不同的文件系统是原子的,但它通常会按预期工作,因为write() 通常在继续之前使用互斥锁锁定内核中的文件描述符。

    【讨论】:

    • 幸运的是,_IOLBF 使 fflush() 变得不必要。代码行数越少,错误就越难隐藏。
    • _IOLBF 没有为我修复它,只有_IONBF /* 没有缓冲。 */ 做。 (注意:使用与 OP 相同的程序,缓冲区为 100 字节)
    • @howaboutsynergy 你可能想用你的代码发布另一个问题。
    • @MaximEgorushkin 代码已经在 OP 中可用(即在问题中),我只是根据您的回答将_IOFBF 替换为_IOLBFAn easy fix would be to open the file in line buffered mode _IOLBF, so that the buffer gets flushed on each complete line. Just make sure that the buffer size is at least as big as your longest line, otherwise it will end up writing incomplete lines.。我只是指出这对我来说显然是不正确的。我注意到libio/fileops.c 有一条评论说:We used to flush all line-buffered stream. This really isn't required by any standard.
    • 如果缓冲区大小 >=128,我不会与 3 种模式中的任何一种进行交错。我会调查原因,但乍一看libio/fileops.c 只显示了一次128 用于写入,但我需要时间来理解逻辑。 /* Try to maintain alignment: write a whole number of blocks. */
    猜你喜欢
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    • 1970-01-01
    • 2010-11-07
    • 1970-01-01
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    相关资源
    最近更新 更多