多个进程写入同一文件(C)
有一个程序(Ubuntu 12.04 LTS,单核处理器):多个进程写入同一文件(C),c,fork,C,Fork,有一个程序(Ubuntu 12.04 LTS,单核处理器): #包括 #包括 #包括 #包括 #包括 int main(){ 模式=S|U IRUSR | S|U IWUSR; int i=0,fd,pid; 无符号字符pi1=0x33,pi2=0x34; 如果((fd=open(“res”,O|U WRONLY | O|U CREAT | O|U TRUNC,mode))
#包括
#包括
#包括
#包括
#包括
int main(){
模式=S|U IRUSR | S|U IWUSR;
int i=0,fd,pid;
无符号字符pi1=0x33,pi2=0x34;
如果((fd=open(“res”,O|U WRONLY | O|U CREAT | O|U TRUNC,mode))<0){
perror(“公开错误”);
出口(1);
}
如果((pid=fork())<0){
perror(“分叉错误”);
出口(1);
}
如果(pid==0){
如果(写入(fd和pi2,1)!=1){
perror(“写入错误”);
出口(1);
}
}否则{
如果(写入(fd和pi1,1)!=1){
perror(“写入错误”);
出口(1);
}
}
关闭(fd);
返回0;
}
其思想是打开文件进行写入,然后进行fork。两个过程总计记录的位置。奇怪的是,如果你运行这个程序,它输出到一个文件“res”并不是常数:我激怒了34然后4然后3。问题是为什么会有这样的结论?(毕竟,如果立场一致,那么结论必须是34或43。)
在我看来,当他找到写入的位置时,该进程在函数write中被中断。当您使用fork()生成多个进程时,无法判断它们将以何种顺序执行。由操作系统调度器决定 因此,让多个进程写入同一个文件会导致灾难
关于为什么有时两个数字中的一个被省略的问题:write首先写入数据,然后递增文件指针。我认为线程控制可能恰好在那一刻更改,以便第二个线程在更新文件位置之前写入。因此它会覆盖另一个进程刚刚写入的数据。您确定需要fork()吗?fork()使用不同的内存空间(文件描述符等)创建不同的进程。也许pthreads适合你?对于pthreads,您将为所有进程共享相同的fd。但无论如何,您应该真正考虑在项目中使用互斥体。以下是我认为正在发生的事情
- 您
文件并打开
分叉
- 父母先跑(如果孩子先跑,类似的事情也会发生)
- 父级写入
并退出3
- 父进程是终端的控制进程,因此内核向前台组的所有成员发送SIGHUP
- 父级写入
的默认操作是终止进程,使子进程安静地死去SIGHUP
sleep(5); /* Somewhere in the child, right before you write. */
您将看到子进程瞬间死亡:从未执行写入操作
另一种测试方法是在分叉之前忽略SIGHUP
,:
sigignore(SIGHUP); /* Define _XOPEN_SOURCE 500 before including signal.h. */
/* You can also use signal(SIGHUP, SIG_IGN); */
您将看到进程现在将两个数字写入文件
覆盖假设不太可能。在
fork
之后,两个进程在系统范围的表中共享指向同一文件描述符的链接,该表还包含文件偏移量我多次运行您的程序,结果是“34”或“43”。
所以我写了一个shell脚本
#!/bin/bash
for i in {1..500}
do
./your_program
for line in $(cat res)
do
echo "$line"
done
done
,并运行程序500次。正如我们所看到的,它有时会得到'3'或'4'(大约是500中的20倍)
我们如何解释这一点?
答案是:
当我们创建子进程时,子进程共享相同的文件描述和文件状态结构(具有当前文件偏移量)。
在正常情况下,进程首先获取偏移量=0,然后写入第一个字节,偏移量=1;另一个进程获取offset=1,它将写入第二个字节。
但有时,如果父进程从文件状态结构获取偏移量=0,而子进程同时获取偏移量=0,则一个进程写入第一个字节,另一个进程覆盖第一个字节。结果将是“3”或“4”(取决于父级是先写入还是子级)。因为它们都写入文件的第一个字节
标准上说
小于等于{PIPE_BUF}字节的写入请求不得与在同一管道上进行写入的其他进程的数据交错。
链接-
写入的原子性仅在写入管道的时间小于等于pipe_BUF的情况下才能得到保证,并且没有为常规文件指定,我们不能假设这一点
因此,在这种情况下,比赛条件正在发生,这将导致某些跑步的数据不正确。
(在我的系统中,它也发生在几千次运行之后)
我想你应该考虑使用互斥体/信号量/任何其他锁定原语来解决这个问题。我激怒了34然后4然后3什么?我希望输出34或43。但有时只出现3或4个。它是如何发生的?我不能100%确定,但可能某个进程覆盖了文件中的数据,该数据是由另一个进程放在那里的(文件中的位置在两个进程之间没有正确共享),而使用此工具的只有这两个进程。它们使用打开文件表中的相同记录。因为一个进程可能会覆盖另一个进程的数据?想知道为什么有时候输出是3或4吗。一个进程在什么时候会覆盖另一个进程的数据?这里的问题是另一个:我希望输出34或43。但有时只出现3或4个。这是怎么发生的?我问你为什么会发生。我很感兴趣为什么会发生这种情况。只要再重新考虑一下共享数据同步。想知道为什么有时候输出是3或4。一个进程在什么时候会覆盖另一个进程的数据?当它执行内核并且没有陷入梦境时,是否有可能停止进程?SIGHUP信号没有发送,因为在父进程之后与终端的连接没有丢失。当我看到配音时,我也很惊讶。毕竟,他们在表格中只使用一个条目
#!/bin/bash
for i in {1..500}
do
./your_program
for line in $(cat res)
do
echo "$line"
done
done