【博客167】IPC(进程间通信)——管道的原子性

内容: 管道其实是内存里的一块环形缓冲区,管道在使用的时候不一定都是原子性的

管道的大小:
在这里插入图片描述
管道的大小就是图中的pipe size: 512bytes × 8 = 4KB,
(其
中,512代表一个扇区的大小,8表示用了8个扇区,所以是4KB,大小可调节)

阻塞与非阻塞对使用管道的区别:

当没有数据可读时
    O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
    O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
当管道满的时候
    O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
    O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

如果所有管道写端对应的文件描述符被关闭,则read返回0
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE

管道的写入原子性:

当要写入的数据量不大于PIPE_BUF时,linux保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

注意:虽然ulimit -a显示管道大小为4KB,但是实际写满是需要写入65536字节满的。也就是你一次性写入
     的容量大于65536字节时,原子性就得不到保障了
     

管道的容量与管道的缓冲区:

管道的大小用ulimit -a查询得到的是:4KB。但是为什么可以写65536个字节呢?

原因:ulimit -a查出来的管道缓冲区大小,也就是pipe_buf的大小,管道容量:pipe_capacity并不是
     这么大。
     pipe_buf :4KB
     pipe_capacity :pipe_buf × pipe_buf的数量 = 4KB × 16 = 65536字节

pipe_buf数量可以在内核的pipe文件中看到:16个 (大家可以在自己的linux系统查pipe头文件查看)
在这里插入图片描述
大数据下管道原子性测试: 借用一下网上的代码修改成小数据和大数据测试

情况一:写入10KB时

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define SIZE 10*1024 //10KB

int main() {

    char A[SIZE];
    char B[SIZE];
         
    memset(A, 'A', sizeof(A));
    memset(B, 'B', sizeof(B));
         
    int pipefd[2];    
    if(pipe(pipefd) < 0) {
        perror("create pipe error\n");    
        return -1;    
    }    
         
    int ret = 0;
         
    pid_t pid = fork();
    if(pid < 0 ) {
        perror("do fork error\n");
        return -1;
    }
    else if(pid == 0) {
        close(pipefd[0]);    
        ret = write(pipefd[1], A, sizeof(A));
        printf("Child process [%d] wrote %d bytes of process_1 to the pipeline.\n", getpid(), ret);    
        exit(0);    
    }        
             
    pid = fork();    
    if(pid < 0 ) {    
        perror("fork error");    
        return -1;    
    }        
    else if(pid == 0) {
        close(pipefd[0]);    
        ret = write(pipefd[1], B, sizeof(B)); 
        printf("Child process [%d] wrote %d bytes of process_2 to the pipeline.\n", getpid(), ret);    
        exit(0);    
    }        
             
    close(pipefd[1]);    
    sleep(1); //休眠1s,确保父子进程的写端都已关闭    
    int n = 0;    
    while(1) {    
        char buf[4*1024] = {0};    
        ret = read(pipefd[0], buf, sizeof(buf)); 
        if(ret == 0) { 
            printf("data read out.\n");    
            break;    
        }    
        printf("%2d: Parent process [%d] read %d bytes from the pipeline, buf[4095] = %c\n", ++n, getpid(), ret, buf[4095]);           
    }        
             
    return 0;    
}


运行结果:此时写入是原子性的
在这里插入图片描述
情况二:写入100KB时
运行结果:此时写入不是原子性的
在这里插入图片描述

大四学生一枚,如果文章有错误的地方,欢迎在下方提出,每条评论我都会去认真看并回复,同时感谢指正的前辈。有喜欢C/C++,linux的同学欢迎私信一起讨论学习。

发布了214 篇原创文章 · 获赞 41 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43684922/article/details/104456998
今日推荐