管道是进程间通信一种基本的的一种方式,管道又分为两种,匿名管道和命名管道,先说匿名管道

匿名管道(pipe)

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

调用pipe时会在内核中开辟一个缓冲区,使用时fileds[0]为输出端,fileds[1]为写入端口,调用成功时返回0,失败时返回-1;

pipe的特点:

1:它只能在有血缘关系的进程间进行通信。

2:它只能进行单项通信,一个进程读,另一个只能写。

3:它是一种流式服务。

4:它的生命周期跟随进程的生命周期。

pipe在使用的时候有四种特殊情况,

1>(代码如下):

如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有

进程 从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像

读文件末一样。

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 #include<string.h>
  6 int main()
  7 {
  8     int status=0;
  9     int _pipe_id[2];
 10     if(pipe(_pipe_id)<0)
 11     {
 12         perror("pipe");
 13         exit(1);
 14     }
 15     pid_t id=fork();
 16     if(id<0)
 17     {
 18         perror("fork");
 19     }
 20     else if(id==0)
 21     {
 22         close(_pipe_id[0]);
 23         char my_buf[]="hello world";
 24         int count=10;
 25         while(count--){
 26             //if(count<5)
 27         //  {
 28                 write(_pipe_id[1],my_buf,strlen(my_buf));
 29         //  }
 30             sleep(1);
 31         }
 32         close(_pipe_id[1]);
 33     }
 34     else
 35     {
 36         close(_pipe_id[1]);
 37         char my_buf[1024];
 38         int count=100;
 39         while(count--)
 40         {
 41             memset(my_buf,'\0',sizeof(my_buf));
 42             ssize_t size= read(_pipe_id[0],my_buf,sizeof(my_buf));
 43                 printf("%d,%s\n",size,my_buf);
 44         }
 45             pid_t _wait_pid=waitpid(id,NULL,0);
 46             if(_wait_pid<0)
 47             {
 48                 return 1;
 49             }
 50     }
 51     return 0;
 52 }

输出结果就是每隔一秒读取子进程写进管道的字符串,read不停地返回零,然后结束

2>(代码将上面的代码的注释去掉)

如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管道写

端的 进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数

据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

3>(代码如下,将上面的父进程代码改成这样)

如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进

程向管道的写端write,那么该进程会收到信号SIGPIPE(读端关闭后,连续有两条写入的指令则会返回这个异常信号量),通常会导致进程异常终止。

 38         int count=5;
 39         while(count--)
 40         {
 41             memset(my_buf,'\0',sizeof(my_buf));
 42             ssize_t size= read(_pipe_id[0],my_buf,sizeof(my_buf));
 43             printf("%d,%s\n",size,my_buf);
 44         }
 45         close(_pipe_id[0]);
 46             pid_t _wait_pid=waitpid(id,&status,0);
 47             printf("status=%d\n",status&0xff);
 48     }

wKiom1cJ7rjRpRR3AABHfYoycKg849.png输出救过如左图显示,信号量13就是SIGPIPE。

4>

如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读

端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回

子进程如下

24         int count=0;
 25         while(1){
 26             count++;
 27             ssize_t size=write(_pipe_id[1],my_buf,strlen(my_buf));
 28             if(size>0)
 29             {
 30             printf("child%d\n",count);      ;
 31             }
 32             else{
 33                 printf("child_count=%d,size=%d\n",count,size);
 34                 sleep(3);
 35             }   
 36         }   
 37     }

父进程如下

 38     else
 39     {
 40             sleep(1);
 41         close(_pipe_id[1]);
 42         char my_buf[1024];
 43         int count=15;
 44         while(count--)
 45         {
 46 
 47                 memset(my_buf,'\0',sizeof(my_buf));
 48                 ssize_t size= read(_pipe_id[0],my_buf,sizeof(my_buf));
 49                 printf("%d,%s\n",size,my_buf);
 50                 sleep(5);
 51         }

wKioL1cJ-p6Rf0q7AAAlvJq_z3w875.png在我的运行环境下(虚拟机CentOS),一次最多向管道里写入了70kb的内容,而后当父进程读取了数据才开始继续存储。


命名管道(FIFO)

因为匿名管道只能在有血缘关系的进程间通信,所以又引入了命名管道,命名管道其实就是通过建立一个公共的双方都能访问的文件来使双方进行通信,有两种方法可以建立这种管道,一个是在程序中调用mknod或mkfifo函数创建一个命名管道,另外一个方法是在shell下交互的建立一个命名管道。

#include <sys/types.h>
#include <sys/stat.h>
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);

函数参数第一个为路径,第二个是权限,一般使用S_IFIFO|0666来创建。

server.c

 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<string.h>
  5 #include<error.h>
  6 #include<fcntl.h>
  7 #include<unistd.h>
  8 #define _PATH_ "./.tmp_fifo"
  9 
 10 int main()
 11 {
 12     umask(0);
 13     if(mkfifo(_PATH_,0666|S_IFIFO)<0)
 14     {
 15         perror("mkfifo");
 16     }
 17     char my_buf[1024];
 18     memset(my_buf,'\0',sizeof(my_buf));
 19         int fd=open(_PATH_,O_WRONLY);
 20         
 21             if(fd<0)
 22             {    
 23                     perror("open");
 24             }
 25 
 26     while(1)
 27     {
 28         printf("please write:");
 29         fflush(stdout);
 30         fgets(my_buf,sizeof(my_buf)-1,stdin);
 31         int size=write(fd,my_buf,strlen(my_buf));
 32     if(size<0)
 33     {
 34         printf("write error!\n");
 35         break;
 36     }
 37     if(strncmp(my_buf,"quit",4)==0)
 38     {
 39         break;
 40     }
 41     }
 42     close(fd);
 43     return 0;     
 44     }

client

 1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<string.h>
  5 #include<error.h>
  6 #include<fcntl.h>
  7 #include<unistd.h>
  8 
  9 #define _PATH_ "./.tmp_fifo"
 10 
 11 int main()
 12 {
 13     int fd=open(_PATH_,O_RDONLY);
 14     if(fd<0)
 15     {
 16         perror("open");
 17     }
 18     char my_buf[1024];
 19     memset(my_buf,'\0',sizeof(my_buf));
 20         while(1)
 21         {
 22         int size=read(fd,my_buf,sizeof(my_buf)-1);
 23                     perror("open");
 24             }
 25 
 26     while(1)
 27     {
 28         printf("please write:");
 29         fflush(stdout);
 30         fgets(my_buf,sizeof(my_buf)-1,stdin);
 31         int size=write(fd,my_buf,strlen(my_buf));
 32     if(size<0)
 33     {
 34         printf("write error!\n");
 35         break;
 36     }
 37     if(strncmp(my_buf,"quit",4)==0)
 38     {
 39         break;
 40     }
 41     }
 42     close(fd);
 43     return 0;
 44 }