进程间通信-匿名管道

Deadly 提交于 2019-11-29 22:11:51

匿名管道

一、特点

1.只适合单向通信。(如果需要双向通信,则需要两个匿名管道来进行完成)

2.只适合具有血缘关系的进程进行通信

3.管道是文件,生命周期随进程,进程结束后,文件就不在了

4.管道是基于字节流方式来进行通信的

5.父进程和子进程访问的公共资源叫做临界资源,所有临界资源都是需要被保护起来的,多个进程进行访问时必须要保证原子性。(任一时刻保证只有一个人访问)

6.管道内部自己已经实现同步性,能保证数据的一致性。

另外,进程间的通信的本质是:两个进程看到了一份公共的资源。

二、管道的创建

1.由父进程创建,子进程继承父进程的文件描述符,指向同一个文件,一个用来读,一个用来写,适当的关闭相应的文件读写端。
这里写图片描述
这里写图片描述

函数功能:创建管道,使得__pipe[0]指向文件的读端,_pipe[1]指向文件的写端
参数:一个含有两个整型元素的数组
返回值:0正常 -1管道创建失败
2.管道的大小
测试匿名管道的大小:

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

int main()
{
        int fd[2];

        int count=0;
        if(pipe(fd)<0)
        {
                perror("Fail to create pipe");
                exit(1);
        }
        while(1)
        {
                write(fd[1],"a",sizeof(char));
                printf("count=%d.\n",++count);
        }
        return 0;
}

这里写图片描述

可知:管道的大小是:65536个字节=2的16次方个字节=64M

三、关于匿名管道读写的四种方式

使⽤用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):

1. 如果所有指向管道写端的⽂文件描述符都关闭了(管道写端的引⽤用计数等于0),⽽而仍然有进程 从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到⽂文件末尾⼀一样。

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>

int main()
{
        int _pipe[2];
        int ret=pipe(_pipe);

        if(ret==-1)
        {
                printf("create pipe error ! error code is :%d\n",errno);
        }

        pid_t id=fork();
        if(id<0)
        {
                printf("fork error\n");
                return 2;
        }
        else if(id==0)//child
        {
                close(_pipe[0]);
                int i=0;
                char * _mesg_c=NULL;

                while(i<10)
                {
                        sleep(1);
                        _mesg_c="i am child";
                        write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
                        i++;

                }
        close(_pipe[1]);
        }
        else//father
        {
                close(_pipe[1]);
                char _mesg[100];
                int j=0;
                printf("child id ;%d\n",id);
                while(j<100)
                {
                        memset(_mesg,'\0',sizeof(_mesg));
                        read(_pipe[0],_mesg,sizeof(_mesg)-1);
                        printf("%s:code count :%d\n",_mesg,j);
                        j++;
                }

        }

        if(waitpid(id,NULL,0)<0)
        {
                printf("wait child sucessfully\n");
                return 3;
        }


        return 0;
}

这里写图片描述
可以看出父进程读取时,发生了阻塞,每隔一秒钟读取一次。

2.如果有指向管道写端的⽂文件描述符没关闭(管道写端的引⽤用计数⼤大于0),⽽而持有管道写端的 进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>

int main()
{
        int _pipe[2];
        int ret=pipe(_pipe);

        if(ret==-1)
        {
                printf("create pipe error ! error code is :%d\n",errno);
        }

        pid_t id=fork();
        if(id<0)
        {
                printf("fork error\n");
                return 2;
        }
        else if(id==0)//child
        {
                close(_pipe[0]);
                int i=0;
                char * _mesg_c=NULL;

                while(i<20)
                {
                        if(i<10)
                        {
                        _mesg_c="i am child";
                        write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
                        }
                        sleep(1);
                        i++;

                }
    //  close(_pipe[1]);
        }
        else//father
        {
///     sleep(3);
                close(_pipe[1]);
                char _mesg[100];
                int j=0;
                printf("child id ;%d\n",id);
                while(j<20)
                {
                        memset(_mesg,'\0',sizeof(_mesg));
                        read(_pipe[0],_mesg,sizeof(_mesg)-1);
                        printf("%s:code count :%d\n",_mesg,j);
                        j++;
                }

        }

        if(waitpid(id,NULL,0)<0)
        {
                printf("wait child sucessfully\n");
                return 3;
        }


        return 0;
}

这里写图片描述

3.如果所有指向管道读端的⽂文件描述符都关闭了(管道读端的引⽤用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终⽌止。

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>

int main()
{
        int _pipe[2];
        int ret=pipe(_pipe);

        if(ret==-1)
        {
                printf("create pipe error ! error code is :%d\n",errno);
        }

        pid_t id=fork();
        if(id<0)
        {
                printf("fork error\n");
                return 2;
        }
        else if(id==0)//child
        {
                close(_pipe[0]);
                int i=0;
                char * _mesg_c=NULL;

                while(i<20)
                {
                        if(i<10)
                        {
                        _mesg_c="i am child";
                        write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
                        }
//                      sleep(1);
                        i++;

                }
        }
        else//father
        {
                close(_pipe[1]);
                char _mesg[100];
                int j=0;
                printf("child id ;%d\n",id);
                while(j<3)
                {
                        memset(_mesg,'\0',sizeof(_mesg));
                        read(_pipe[0],_mesg,sizeof(_mesg)-1);
                        printf("%s:code count :%d\n",_mesg,j);
                        j++;
                }
                close(_pipe[0]);

        }

        if(waitpid(id,NULL,0)<0)
        {
                printf("wait child sucessfully\n");
                return 3;
        }
        return 0;
}

这里写图片描述
进程异常终止

4.如果有指向管道读端的⽂文件描述符没关闭(管道读端的引⽤用计数⼤大于0),⽽而持有管道读端的 进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再 次write会阻塞,直到管道中有空位置了才写⼊入数据并返回。

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/wait.h>
#include<unistd.h>

int main()
{
        int _pipe[2];
        int ret=pipe(_pipe);

        if(ret==-1)
        {
                printf("create pipe error ! error code is :%d\n",errno);
        }

        pid_t id=fork();
        if(id<0)
        {
                printf("fork error\n");
                return 2;
        }
        else if(id==0)//child
        {
                close(_pipe[0]);
                int i=0;
                char * _mesg_c=NULL;

                while(i<20)
                {
                        _mesg_c="i am child";
                        write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
                        sleep(1);
                        i++;

                }
        }
        else//father
        {
                close(_pipe[1]);
                char _mesg[100];
                int j=0;
                printf("child id ;%d\n",id);
                while(j<5)
                {
                        memset(_mesg,'\0',sizeof(_mesg));
                        read(_pipe[0],_mesg,sizeof(_mesg)-1);
                        printf("%s:code count :%d\n",_mesg,j);
                        j++;
                }

        }

        if(waitpid(id,NULL,0)<0)
        {
                printf("wait child sucessfully\n");
                return 3;
        }


        return 0;
}

这里写图片描述

写入端发生阻塞,一直等待读取。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!