2

我正在尝试在写入文件时模拟竞争条件。这就是我正在做的事情。

  1. 在process1中以追加模式打开a.txt
  2. 在 process1 中写“hello world”
  3. 在 process1 中打印 ftell,即 11
  4. 让process1进入睡眠状态
  5. 在 process2 中以追加模式再次打开 a.txt
  6. 在 process2 中写入“hello world”(正确附加到文件末尾)
  7. 在 process2 中打印 ftell 为 22(正确)
  8. 在 process2 中写“再见世界”(这正确地附加到文件的末尾)。
  9. 进程2退出
  10. process1 恢复并打印其 ftell 值,即 11。
  11. 用 process1 写“再见世界” --- 我假设 process1 的 ftell 是 11,这应该覆盖文件。

但是,process1 的写入是写入文件末尾,进程之间没有写入争用。

我使用 fopen 作为fopen("./a.txt", "a+)

谁能告诉我为什么会出现这种行为以及如何在写入文件时模拟竞争条件?

进程1的代码:

#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
#include "time.h"
using namespace std;
int main()
{

    FILE *f1= fopen("./a.txt","a+");
    cout<<"opened file1"<<endl;
    string data ("hello world");
    fwrite(data.c_str(), sizeof(char), data.size(),  f1);
    fflush(f1);
    cout<<"file1 tell "<<ftell(f1)<<endl;
    cout<<"wrote file1"<<endl;
    sleep(3);
    string data1 ("bye world");;
    cout<<"wrote file1 end"<<endl;
    cout<<"file1 2nd tell "<<ftell(f1)<<endl;
    fwrite(data1.c_str(), sizeof(char),  data1.size(),  f1);
    cout<<"file1 2nd tell "<<ftell(f1)<<endl;
    fflush(f1);
    return 0;
}

在process2中,我已经注释掉了该sleep声明。

我正在使用以下脚本运行:

./process1 &
sleep 2
./process2 &

谢谢你的时间。

4

4 回答 4

2

以追加模式写入是一种原子操作。这就是它不会破裂的原因。

现在......如何打破它?

尝试内存映射文件并从两个进程写入内存。我很确定这会破坏它。

于 2012-12-22T01:25:42.633 回答
2

作者代码:

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

#define BLOCKSIZE 1000000

int main(int argc, char **argv)
{
    FILE *f = fopen("a.txt", "a+");
    char *block = malloc(BLOCKSIZE);

    if (argc < 2)
    {
    fprintf(stderr, "need argument\n");
    }
    memset(block, argv[1][0], BLOCKSIZE);
    for(int i = 0; i < 3000; i++)
    {
    fwrite(block, sizeof(char), BLOCKSIZE, f);
    }
    fclose(f);
}

阅读器功能:

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

#define BLOCKSIZE 1000000

int main(int argc, char **argv)
{
    FILE *f = fopen("a.txt", "r");
    int c;
    int oldc = 0;
    int rl = 0;

    while((c = fgetc(f)) != EOF)
    {
    if (c != oldc)
    {
        if (rl)
        {
        printf("Got %d of %c\n", rl, oldc);
        }
        oldc = c;
        rl = 0;
    }
    rl++;
    }

    fclose(f);
}

./writefile A & ./writefile B我当时跑./readfile

我懂了:

Got 1000999424 of A
Got 999424 of B
Got 999424 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A

正如你所看到的,A 和 B 有很长的运行,但它们并不完全是 1000000 个字符长,这是我写它们的大小。整个文件,在第一次运行较小的试运行后,只有 7GB。

参考:Fedora Core 16,我自己编译的 3.7rc5 内核,gcc 4.6.3,x86-64 和 lvm 之上的 ext4,AMD PhenomII 四核处理器,16GB 内存

于 2012-12-22T02:04:01.693 回答
1

我很确定你不能依赖这种行为,但它可能在某些系统上可靠地工作。如果您“足够努力”,从两个不同的进程写入同一个文件可能迟早会导致问题。和草皮定律说,这正是你的老板检查软件是否正常工作的时候,你的客户接收你出售的系统的时候,或者你完成需要很长时间才能生成的报告的时候,或者其他一些重要的时间。

于 2012-12-22T01:19:51.683 回答
1

您尝试破坏或查看的行为取决于您正在使用的操作系统,因为写入文件是系统调用。根据您告诉我们的第一个文件描述符不覆盖第二个进程写入的内容,您在两个进程中以附加模式打开文件的事实可能在实际写入之前实现了 ftell 值。

您是否尝试对标准的 open 和 write 函数做同样的事情?可能也很有趣。

编辑:C++ 参考文档在这里解释了 fopen 追加选项:“追加/更新:打开一个文件进行更新(输入和输出),所有输出操作都在文件末尾写入数据。重新定位操作(fseek,fsetpos , rewind) 影响下一个输入操作,但输出操作将位置移回文件末尾。” 这解释了您观察到的行为。

于 2012-12-22T01:34:56.123 回答