一、问题还原
在多进程的环境下,父子进程同时去写一个文件,例如父进程每次写入aaaaa,子进程每次写入bbbbb,问题是会不会出现写操作被打断的现象,比如出现aabbbaaabb这样交替的情况?
二、结论
1:使用write系统调用的情况下,不会出现内容交叉的情况。
2:使用fwriteANSIC标准C语言函数,会出现内容交叉的情况。
三、实验过程
实验环境:
操作系统: RedHat Linux 7.0
实验过程:
1:打开一个文件,fork一个子进程,父子进程同时写文件,父进程写入a,子进程写入b。
2:分别用write和fwrite去观察现象。
实验现象
write:不会出现数据交叉的情况,而且父子进程交替执行写入。
1:测试代码如下:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include<sys/wait.h>int main(int argc,char * argv[])
{struct timeval start,end;int times = argc > 1 ? atoi(argv[1]):10000; //通过参数传入需要写入的字节数int stat;int fd;int childpid;int i;for(i=0 ;i<1; i++){if(childpid = fork())break;}if(childpid == -1){perror("failed to fork\n");return 1;}fd = open("tmp.dat",O_WRONLY|O_CREAT|O_APPEND,0666);if(fd < 0){perror("failed to open\n");return 1;}gettimeofday(&start,NULL); //测试下时间if(childpid > 0){char *buf = (char*)malloc(times);for(int i = 0;i < times;++i) {buf[i] = 'a';}strcat(buf,"\n");for(i=0; i<10; i++){usleep(1000);write(fd,buf,strlen(buf));}wait(&stat);}else{char *buf = (char*)malloc(times);for(int i = 0;i < times;++i) {buf[i] = 'b';}strcat(buf,"\n");for(i=0; i<10; i++){usleep(1000);write(fd,buf,strlen(buf));}}close(fd);gettimeofday(&end,NULL);int timeuse = 1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec;printf("UseTime: MicroSeconds:%d us and Seconds:%d s\n",timeuse,end.tv_sec-start.tv_sec);return 0;
}
2:编译运行
$ gcc file.c -std=c99 -o file
$ ./file 100
3:结果
可以发现首先没有出现交叉的情况,并且父子进程是交替写入的,即一行a,一行b。
fwrite:在写入的字节数为500的时候就会出现交叉的情况(当然,500并不是最准确的数字,只是我测试500的时候已经出现了)。
1:测试代码如下:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>int main(int argc,char * argv[])
{struct timeval start,end;int times = argc > 1 ? atoi(argv[1]):10000;int stat;int fd;int childpid;int i;for(i=0 ;i<1; i++){if(childpid = fork())break;}if(childpid == -1){perror("failed to fork\n");return 1;}FILE *fp = NULL;fp = fopen("tmpfp.dat","ab");if(fp == NULL) {system("touch tmpfp.dat");}gettimeofday(&start,NULL); if(childpid > 0){char *buf = (char*)malloc(times);for(int i = 0;i < times;++i) {buf[i] = 'a';}strcat(buf,"\n");for(i=0; i<10; i++){usleep(1000);fwrite(buf,strlen(buf),1,fp);}wait(&stat);}else{char *buf = (char*)malloc(times);for(int i = 0;i < times;++i) {buf[i] = 'b';}strcat(buf,"\n");for(i=0; i<10; i++){usleep(1000);fwrite(buf,strlen(buf),1,fp);}}fclose(fp);gettimeofday(&end,NULL); int timeuse = 1000000 * (end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec;printf("UseTime: MicroSeconds:%d us and Seconds:%d s\n",timeuse,end.tv_sec-start.tv_sec);return 0;
}
2:编译运行
$ gcc fileFP.c -std=c99 -o Filefp
$ ./Filefp 500
3:结果
用一个sed的表达式判断下是否出现了交叉现象,因为我们是以行写入的,所以每行的开头不是a就是b,拿出所有a开头的行,看里面是否有包含b的。
$ sed -n '/^a.*$/p' tmpfp.dat | grep b// '/^a.*$/p' 表示以a开头
// grep b 表示过滤出包含b的行
可以看到,已经出现了一行a中混入了b,因此fwrite在多进程的情况下操作同一个fd是会出现问题的。
四、反思
1:为什么write不会出现问题但是fwrite却出现了问题?
答:write是Linux操作系统的系统调用,fwrite是ANSIC标准的C语言库函数,fwrite在用户态是有缓冲区的。因此需要锁机制来保证并发环境下的安全访问。
http://www.cnblogs.com/ldp-web/archive/2011/10/21/2220180.html
2:如果两个进程同时write一个socket会怎样?
答:就像队列一样,一个进程写完另一个进程才能写,数据上不会有问题。
http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
参考资料:
http://bbs.chinaunix.net/thread-804742-1-1.html
http://www.chinaunix.net/old_jh/23/829712.html
https://www.nowcoder.com/questionTerminal/869cae279aa84d8b8e9e50cf1084830b
《新程序员》:云原生和全面数字化实践50位技术专家共同创作,文字、视频、音频交互阅读
总结
以上是生活随笔为你收集整理的多进程同时写一个文件会怎样?分别用write和fwrite去观察现象的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。