温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

IPC之管道

发布时间:2020-08-11 18:19:40 来源:网络 阅读:1220 作者:SherryX 栏目:系统运维

现在linux使用的IPC(Inter-Process Communication,进程间通信)方式有以下几种:
-(1)管道(pipe)和匿名管道(FIFO)
-(2)信号(signal)
-(3)消息队列
-(4)共享内存
-(5)信号量
-(6)套接字(socket)

什么是管道

管道是Unix中最古老的进程间通信的形式。我们把一个进程连接到另一个进程的一个数据流成为一个“管道”。

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信的时候,需要建立其两个管道。
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)进行通信。

    pipe函数

       #include <unistd.h>
       int pipe(int pipefd[2]);

    功能:创建无名管道
    参数:文件描述符组。fd[0]表示读端,fd[1]表示写端。
    返回值:成功返回0,失败返回错误代码

    也就是说,在fork()之前pipe(),就可以使得父子进程之间建立起一个管道,画个图:
    IPC之管道
    父子进程都会打开5个文件描述符,除了默认的0、1、2。还有fd[0]、fd[1]。测试一下,就会知道这两个文件描述符为3、4。
    写串代码用一用:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    int main()
    {
    int fd[2];
    int retByte;
    pid_t pid;
    char buf[20] = "";
    
    pipe(fd);   /*创建无名管道*/
    //printf("%d,%d\n",fd[0],fd[1]);
    
    pid = fork();
    if(pid == -1)
    {
        perror("create fork");
        return -1;
    }
    if(pid == 0)
    {
        //子进程,写端,使用fd[1]
        //close(fd[0]);
        //close(fd[1]);
        while(1)
        {
            scanf("%s",buf);
            if( write(fd[1],buf,strlen(buf)) == -1)
            {
                perror("write");
                return -1;
            }
            memset(buf,0,20);
            if(read(fd[0],buf,5) > 0 )
            {
                printf("child-read msg: %s\n",buf);
            }
        }
    }
    else
    {
        //父进程,读端,使用fd[0]
        while(1)
        {
            memset(buf,0,20);
            retByte = read(fd[0],buf,5);//每次只读5个
            if( retByte == -1)  
            {
                perror("read");
                return -1;
            }
            if(retByte > 0)
            {
                printf("parent-read msg: %s\n",buf);
            }
        }
    }
    return 0;
    }

    运行结果:
    IPC之管道
    那么,如果没有读端呢?也就是父子进程的fd[0]都关闭了,会有什么现象呢?

    void handler(int no)
    {
        printf("SIGPIPE.\n");
    }
    
    int main()
    {
            int fd[2];
            int retByte;
            int rlt;
            pid_t pid;
            char buf[20] = "";
    
            rlt = pipe(fd); /*创建无名管道*/
            //printf("%d,%d\n",fd[0],fd[1]);
    
            signal(SIGPIPE,handler);
    
            if(rlt != 0)
            {
                perror("pipe");
                return -1;
            }
    
            pid = fork();
            if(pid == -1)
            {
                perror("create fork");
                return -1;
            }
            if(pid == 0)
            {
                //子进程,写端,使用fd[1]
                close(fd[0]);
                //close(fd[1]);
                while(1)
                {
                    scanf("%s",buf);
                    if( write(fd[1],buf,strlen(buf)) == -1)
                    {
                        perror("write");
                        return -1;
                    }
                    memset(buf,0,20);
                }
            }
            else
            {
                //父进程,读端,使用fd[0]
                close(fd[0]);
                while(1)
                {
                }
            }
            return 0;
    }

    运行结果:
    IPC之管道
    会出现管道破裂!!(如果没有重写管道破裂的处理函数,系统默认的处理方式就是杀死进程,父子进程都over了)

    管道读写规则

    所以,总结一下读写规则:
    读规则:
    1)缓冲区没数据:阻塞
    2)缓冲区的数据少于请求字节数:缓冲区有多少就读多少
    3)缓冲区的数据多于请求字节数:只读取请求字节数,剩下的还在缓冲区
    4)写端关闭:读端等待。
    写规则:
    1)缓冲区满了:写不进去
    2)没有读端:管道破裂,父子进程都结束了。调试到write,发生SIGPIPE。
    注意:读端和写端的对应关系可以是一对一、一对多、多对一、多对多的。

    练习

    上面讲到,缓冲区如果满了,就写不进去了。那么缓冲区有多大呢?换言之,如何检测linux中管道的容量?
    代码如下:

    int main()
    {
    int fd[2];
    pipe(fd);
    char buf[4096]; //4k
    int i,loop,ret;
    
    for(i = 0 ; i < sizeof(buf) ; i++)
    {
        buf[i] = 'a';
    }
    
    loop = 100 ; //如果循环结束,还没阻塞,增加循环次数
    for(i = 0; i < loop ; i++)
    {
        ret = write(fd[1],buf,sizeof(buf));
        if(ret == -1)
        {
            perror("write error!\n");
            return 1;
        }
        else
        {
            printf("write successfully!    ");
            printf("size: %d K\n", (i+1)*4);
        }
    }
    
    close(fd[0]);
    close(fd[1]);
    
    return 0;
    }

    运行结果:
    IPC之管道
    在写完64k的时候出现阻塞,说明管道已经满了。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI