管道由pipe函数创建

 #include<unistd.h>

 int pipe(int filedes[2]);

调用pipe函数在内核中开辟一块缓冲区(就是管道)用于通信,filedes[0]指向管道的读端,filedes[1]指向管道的写端。pipe函数调用成功返回0,调用失败返回-1。

比如,父进程关闭读端,子进程关闭写端。代码如下:

  1   #include<stdio.h>

  2   #include<string.h>

  3   #include<unistd.h>

  4   #include<sys/types.h>

  5   #include<stdlib.h>

  6   int main()

  7   {

  8            int _pipe_fd[2]={-1,-1};

  9            if(pipe(_pipe_fd)<0)

 10            {

 11                   perror("pipe");

 12                   exit(1);

 13            }

 14           pid_t _pid=fork();

 15           if(_pid<0)

 16           {

 17                   perror("fork");

 18                   exit(2);

 19           }

 20           else if(_pid==0)

 21           {

 22                   close(_pipe_fd[0]);

 23                   int count=10;

 24                   char buf[]="hello world";

 25                   while(count--)

 26                   {

 27                           write(_pipe_fd[1],buf,strlen(buf));

 28                           sleep(1);

 29                   }

 30                   exit(0);

 31           }

 32           else

 33           {

 34                   close(_pipe_fd[1]);

 35                   char buf[1024];

 36                   while(1)

 37                   {

 38                           memset(buf,'\0',sizeof(buf));

 39                           ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);

 40                           if(_size>0)

 41                           {

 42                                   buf[_size]='\0';

 43                                   printf("%s\n",buf);

 44                           }

 45                   }

 46           }

 47   }

运行结果:

wKiom1cTQQeRIeSjAAAZ0wVBmYg043.png

使用管道有限制:两个进程通过一个管道只能实现单向通信。

使用管道需要注意以下四种情况:

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

代码如下:

  1   #include<stdio.h>

  2   #include<string.h>

  3   #include<unistd.h>

  4   #include<sys/types.h>

  5   #include<stdlib.h>

  6   int main()

  7   {

  8            int _pipe_fd[2]={-1,-1};

  9            if(pipe(_pipe_fd)<0)

 10            {

 11                   perror("pipe");

 12                   exit(1);

 13            }

 14           pid_t _pid=fork();

 15           if(_pid<0)

 16           {

 17                   perror("fork");

 18                   exit(2);

 19           }

 20           else if(_pid==0)

 21           {

 22                   close(_pipe_fd[0]);

 23                   int count=10;

 24                   int i=0;

 25                   char buf[]="hello world";

 26                   while(count--)

 27                   {

 28                         if(i==5)

 29                         {

 30                                 printf("I want to sleep");

 31                                 break;

 32                         }

 33                           write(_pipe_fd[1],buf,strlen(buf));

 34                           sleep(1);

 35                           i++;

 36                   }

 37                 close(_pipe_fd[1]);

 38           }

 39           else

 40           {

 41                   close(_pipe_fd[1]);

 42                   char buf[1024];

 43                   while(1)

 44                   {

 45                           memset(buf,'\0',sizeof(buf));

 46                           ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);

 47                           if(_size>0)

 48                           {

 49                                   buf[_size]='\0';

 50                                   printf("%s\n",buf);

 51                           }

 52                   }

 53           }

 54   }

运行结果:

wKioL1cTReWSTEpeAAAgrqjr5uk373.png

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

代码如下:

1   #include<stdio.h>

  2   #include<string.h>

  3   #include<unistd.h>

  4   #include<sys/types.h>

  5   #include<stdlib.h>

  6   int main()

  7   {

  8            int _pipe_fd[2]={-1,-1};

  9            if(pipe(_pipe_fd)<0)

 10            {

 11                   perror("pipe");

 12                   exit(1);

 13            }

 14           pid_t _pid=fork();

 15           if(_pid<0)

 16           {

 17                   perror("fork");

 18                   exit(2);

 19           }

 20           else if(_pid==0)

 21           {

 22                   close(_pipe_fd[0]);

 23                   int count=10;

 24                   int i=0;

 25                   char buf[]="hello world";

 26                   while(count--)

 27                   {

 28                           if(i==5)

 29                           {

 30                                 printf("I am child\n");

 31                                 sleep(10);

 32                           }

 33                           write(_pipe_fd[1],buf,strlen(buf));

 34                           sleep(1);

 35                           i++;

 36                   }

 37                   exit(0);

 38                   close(_pipe_fd[1]);

 39           }

 40           else

 41           {

 42                   close(_pipe_fd[1]);

 43                   char buf[1024];

 44                   while(1)

 45                   {

 46                           memset(buf,'\0',sizeof(buf));

 47                           ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);

 48                           if(_size>0)

 49                           {

 50                                   buf[_size]='\0';

 51                                   printf("%s\n",buf);

 52                           }

运行结果:

wKioL1cTSBOBFT52AAAlFPkv0pk197.png

3.如果指向所有管道读端的文件描述符都关闭了(管道读端的文件描述符为0),当有进程向管道的写端写数据时,该进程会收到信号SIGPIPE,通常会导致进程异常终止。

代码如下:

  1   #include<stdio.h>

  2   #include<string.h>

  3   #include<unistd.h>

  4   #include<sys/types.h>

  5   #include<stdlib.h>

  6   int main()

  7   {

  8            int _pipe_fd[2]={-1,-1};

  9            if(pipe(_pipe_fd)<0)

 10            {

 11                   perror("pipe");

 12                   exit(1);

 13            }

 14           pid_t _pid=fork();

 15           if(_pid<0)

 16           {

 17                   perror("fork");

 18                   exit(2);

 19           }

 20           else if(_pid==0)

 21           {

 22                   close(_pipe_fd[0]);

 23                   int count=10;

 24                   char buf[]="hello world";

 25                   while(count--)

 26                   {

 27                           write(_pipe_fd[1],buf,strlen(buf));

 28                           sleep(1);

 29                   }

 30                   close(_pipe_fd[1]);

 31           }

 32           else

 33           {

 34                   close(_pipe_fd[1]);

 35                   char buf[1024];

 36                   int j=5;

 37                   while(j--)

 38                   {

 39                           if(j==3)

 40                           {

 41                                 close(_pipe_fd[0]);

 42                           }

 43                           memset(buf,'\0',sizeof(buf));

 44                           ssize_t _size=read(_pipe_fd[0],buf,sizeof(buf)-1);

 45                           if(_size>0)

 46                           {

 47                                   buf[_size]='\0';

 48                                   printf("%s\n",buf);

 49                           }

 50                   }

 51                 int status=0;

 52                 pid_t _id=waitpid(_pid,&status,0);

 53                 printf("waitpid:%d,status:%d\n",_id,status&0xff);

 54           }

 55         return 0;

 56   }

 运行结果:

wKiom1cTSS3CFrjSAAAefZppTeE788.png

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

代码如下:

  1   #include<stdio.h>

  2   #include<string.h>

  3   #include<unistd.h>

  4   #include<sys/types.h>

  5   #include<stdlib.h>

  6   int main()

  7   {

  8            int _pipe_fd[2]={-1,-1};

  9            if(pipe(_pipe_fd)<0)

 10            {

 11                   perror("pipe");

 12                   exit(1);

 13            }

 14           pid_t _pid=fork();

 15           if(_pid<0)

 16           {

 17                   perror("fork");

 18                   exit(2);

 19           }

 20           else if(_pid==0)

 21           {

 22                   close(_pipe_fd[0]);

 23                   int count=10;

 24                   char buf[]="hello world";

 25                   while(count--)

 26                   {

 27                           write(_pipe_fd[1],buf,strlen(buf));

 28                           sleep(1);

 29                   }

 30                  close(_pipe_fd[0]);

 31           }

 32           else

 33           {

 34                 close(_pipe_fd[1]);

 35                 int status=0;

 36                 pid_t id=waitpid(_pid,&status,0);

 37                 if(id==_pid)

 38                 {

 39                         printf("id:%d,status:%d",id,status);

 40                 }

 41 

 42           }

 43 }

运行结果:

wKiom1cTTmGxTLsMAAAfI3ttJI0284.png

总结:匿名管道有以下特点:

(1)匿名管道提供的通信是单向的;

(2)匿名管道通信用于有关系的进程间,通常用于父子进程之间(命名管道将针对于这一问题进行解决);

(3)匿名管道能够实现进程间的同步与互斥;

(4)匿名管道的生命周期随进程。