Linux write()系統調用是否是原子性及其O_APPEND參數實驗驗證

基於該博客的結論進行實驗驗證
首先需要各位前去看鏈接中的博客,瞭解裏面的結論和知識點再來看我的實驗驗證。

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方式打開即可。

我們針對博客中的這些結論進行實驗驗證
實驗環境 : 在這裏插入圖片描述

  • 驗證思路
  1. 用fork()創建進程共享打開的文件描述符寫入數據進行實驗;
  2. 用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博主

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章