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博主

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