一、問題還原
在多進程的環境下,父子進程同時去寫一個文件,例如父進程每次寫入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去观察现象的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。