基於該博客的結論進行實驗驗證
首先需要各位前去看鏈接中的博客,瞭解裏面的結論和知識點再來看我的實驗驗證。
write調用能保證的是,不管它實際寫入了多少數據,比如寫入了n字節數據,在寫入這n字節數據的時候,在所有共享文件描述符的線程或者進程之間,每一個write調用是原子的,不可打斷的。舉一個例子,比如線程1寫入了3個字符’a’,線程2寫入了3個字符’b’,結果一定是‘aaabbb’或者是‘bbbaaa’,不可能是類似‘abaabb’這類交錯的情況。
也許你自然而然會問一個問題,如果兩個進程沒有共享文件描述符呢?比如進程A和進程B分別獨立地打開了一個文件,進程A寫入3個字符’a’,進程B寫入了3個字符’b’,結果怎樣呢?
答案是,這種情況下沒有任何保證,最終的結果可能是‘aaabbb’或者是‘bbbaaa’,也可能是‘abaabb’這種交錯的情況。如果你希望不交錯,那麼怎麼辦呢?答案也是有的,那就是在所有寫進程打開文件的時候,採用O_APPEND方式打開即可。
我們針對博客中的這些結論進行實驗驗證
實驗環境 :
- 驗證思路
- 用fork()創建進程共享打開的文件描述符寫入數據進行實驗;
- 用fork()分別再父子進程中打開文件描述符寫入數據進行實驗。
- 該程序共享文件描述符
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<string.h>
int main()
{
int fd = open("test", O_RDWR, 0666);
pid_t pid = fork();
if(pid == 0)
{
//int fd = open("test", O_RDWR | O_APPEND, 0666);
char buffer[200];
memset(buffer, 'a', 200);
write(fd, buffer, 200);
exit(0);
}
if(pid > 0)
{
//int fd = open("test", O_RDWR | O_APPEND, 0666);
if(fd > 0)
std::cout << "parent open file success\n";
char buffer[16];
memset(buffer, '=', 16);
write(fd,buffer, 16);
}
std::cout << "寫入完成\n";
int ret;
wait(&ret);
}
實驗結果如下:
多次實驗得出結論 ,查看test文件的字節數,發現是216和我們寫入的字節數相同,並且數據沒有混亂。共享文件描述符保證原子序,但是不保證寫入的順序性,這個由操作系統調度進程順序決定。
- 不共享文件描述符進行實驗
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<string.h>
int main()
{
//int fd = open("test", O_RDWR, 0666);
pid_t pid = fork();
if(pid == 0)
{
int fd = open("test", O_RDWR, 0666);
char buffer[200];
memset(buffer, 'a', 200);
write(fd, buffer, 200);
exit(0);
}
if(pid > 0)
{
int fd = open("test", O_RDWR, 0666);
if(fd > 0)
std::cout << "parent open file success\n";
char buffer[16];
memset(buffer, '=', 16);
write(fd,buffer, 16);
}
std::cout << "寫入完成\n";
int ret;
wait(&ret);
}
實驗結果如下:
多次試驗結果得出結論,不共享文件描述符,寫入的數據可能會被覆蓋也可能會交錯,查看test文件發現只有200個字節被寫入文件中,所以在寫進程中打開文件要加上O_APPEND,避免出現上述錯誤
下圖實驗結果爲每個進程打開文件加上O_APPEND標誌位,結果如圖所示
可以看出結果和共享文件描述符相同,保證寫入的數據正確性。
- 多線程程序實驗
實驗思路:
共享文件描述符: 主線程創建文件描述符通過參數傳遞給線程函數實現共享,線程函數向該fd中寫入數據
非共享的文件描述符:線程函數中分別打開fd,向其寫入數據進行實驗
共享文件描述符實驗代碼
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/unistd.h>
#include<thread>
#include<string.h>
void* func(int fd)
{
/*
int fd = open("test1", O_RDWR, 0666);
if(fd > 0)
std::cout << "func fd create success\n";
*/
char buffer[100];
memset(buffer, 'a', 20);
write(fd, buffer, 20);
}
void* func2(int fd)
{
/*
int fd = open("test1", O_RDWR, 0666);
if(fd > 0)
std::cout << "func2 fd create success\n";
*/
char buffer[100];
memset(buffer, '=', 30);
write(fd, buffer, 30);
}
int main()
{
int fd = open("test1", O_RDWR, 0666);
std::thread thread1(func, fd);
std::thread thread2(func2, fd);
std::thread thread3(func, fd);
std::thread thread4(func2, fd);
std::thread thread5(func, fd);
std::thread thread6(func2, fd);
thread1.join();
thread2.join();
thread3.join();
thread4.join();
thread5.join();
thread6.join();
getchar();
return 0;
}
實驗結果如圖所示:
發現寫入數據沒有混亂並且寫入了150個字節的數據,和我們寫入的數量相同。
非共享文件描述符實驗代碼
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/unistd.h>
#include<thread>
#include<string.h>
void* func()
{
int fd = open("test1", O_RDWR, 0666);
if(fd > 0)
std::cout << "func fd create success\n";
char buffer[100];
memset(buffer, 'a', 20);
write(fd, buffer, 20);
}
void* func2()
{
int fd = open("test1", O_RDWR, 0666);
if(fd > 0)
std::cout << "func2 fd create success\n";
char buffer[100];
memset(buffer, '=', 30);
write(fd, buffer, 30);
}
int main()
{
std::thread thread1(func);
std::thread thread2(func2);
std::thread thread3(func);
std::thread thread4(func2);
std::thread thread5(func);
std::thread thread6(func2);
thread1.join();
thread2.join();
thread3.join();
thread4.join();
thread5.join();
thread6.join();
getchar();
return 0;
}
實驗結果如圖所示:
實際應該寫入的數據長度爲150個字節,而現在只有30個字節,數據寫入混亂。
非共享文件描述符加入O_APPEND參數打開,試驗結果如下圖所示:
發現和共享描述符的寫入結果相同,文章開頭的結論得以驗證!
本篇博客僅爲本人實驗驗證後的結論,如有錯誤請指出,感謝!
參考文獻:
談談Linux IO 文章來自阿里學長
write及O_APPEND參數 來自CSDN博主