我试图模拟写文件时的竞争条件。这就是我正在做的。
- 在process1 中以追加方式打开a.t txt
- 在process1中写入"hello world"
- 打印process1中的ftell,它是11
- 将process1置于睡眠状态 在process2中以追加模式再次打开a.t txt
- 在process2中写入"hello world"(这正确地附加到文件的末尾)
- 打印process2中的ftell,它是22(正确)
- 在process2中写入"bye world"(这正确地附加到文件的末尾)。
- process2退出
- process1恢复,并打印ftell值,即11。
- 通过process1写入"bye world"——我假设process1的ftell是11,这应该覆盖文件。
然而,process1的写操作正在写到文件的末尾,并且进程之间没有写争用。
我使用fopen作为fopen("./a.txt", "a+)
有谁能告诉我为什么会出现这种行为,以及我如何在写入文件时模拟竞争条件?
process1的代码:#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 &
感谢您的宝贵时间。
写入器代码:
#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 argumentn");
}
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 %cn", rl, oldc);
}
oldc = c;
rl = 0;
}
rl++;
}
fclose(f);
}
我先跑./writefile A & ./writefile B
,再跑./readfile
I got this:
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和ext4在lvm之上,AMD PhenomII四核处理器,16GB RAM
以追加方式写入是原子操作。这就是为什么它不会破裂。
现在…如何打破它?
尝试内存映射文件并从两个进程写入内存。
我很确定您不能依赖这种行为,但它可能在某些系统上可靠地工作。如果您"足够努力"的话,从两个不同的进程向同一个文件写入可能迟早会导致问题。sod's law说,这正是你的老板检查软件是否工作的时候,当你的客户收到你销售的系统的时候,或者当你完成花了很长时间才完成的报告的时候,或者其他一些重要的时间。
你试图打破或看到的行为取决于你在哪个操作系统上工作,因为在文件中写入是一个系统调用。根据你告诉我们的第一个文件描述符不覆盖第二个进程所写的内容,你在两个进程中以追加模式打开文件的事实可能在实际写入之前已经实现了ftell值。
您是否尝试对标准的打开和写入函数做同样的事情?可能也很有趣。
编辑:c++参考文档解释了fopen追加选项:append/update:打开一个文件进行更新(包括输入和输出),所有的输出操作都在文件的末尾写入数据。重定位操作(fseek, fsetpos, rewind)会影响下一个输入操作,但输出操作会将位置移回文件末尾。这解释了您观察到的行为。