2

我用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 函数而被中断。

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

提前致谢!

4

2 回答 2

2

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

引用POSIXwrite()

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

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

于 2012-01-22T11:27:10.020 回答
0

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

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

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

于 2012-01-22T14:00:34.243 回答