千家信息网

IPC之管道

发表于:2025-01-29 作者:千家信息网编辑
千家信息网最后更新 2025年01月29日,现在linux使用的IPC(Inter-Process Communication,进程间通信)方式有以下几种:-(1)管道(pipe)和匿名管道(FIFO)-(2)信号(signal)-(3)消息队
千家信息网最后更新 2025年01月29日IPC之管道

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

什么是管道

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

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

    pipe函数

       #include    int pipe(int pipefd[2]);

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

    也就是说,在fork()之前pipe(),就可以使得父子进程之间建立起一个管道,画个图:

    父子进程都会打开5个文件描述符,除了默认的0、1、2。还有fd[0]、fd[1]。测试一下,就会知道这两个文件描述符为3、4。
    写串代码用一用:

    #include #include #include #include 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;}

    运行结果:

    那么,如果没有读端呢?也就是父子进程的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;}

    运行结果:

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

    管道读写规则

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

    练习

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

    int main(){int fd[2];pipe(fd);char buf[4096]; //4kint 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;}

    运行结果:

    在写完64k的时候出现阻塞,说明管道已经满了。

0