一.管道是进程间通信的一种重要手段,在linux中没有使用专门的数据结构,而是借助了文件系统的file结构和VFS索引节点inode。通过两个file结构指向同一个临时的VFS索引节点,而这个索引节点又指向一个物理页面实现的。如下图所示:


wKiom1cInrnwETKlAABLoSbbw2A646.png


管道的实现的源代码在fs/pipe.c中,其中pipe_read()和pipe_write()是管道的读写函数。管道写函数通过将字节复制到 VFS 索引节点指向的物理内存而写入数据,而管道读函数则通过复制物理内存中的字节而读出数据。当然,内核必须利用一定的机制同步对管道的访问,为此,内核使用了锁、等待队列和信号。

二.匿名管道(pipe)

   常用于父子进程,也用于有血缘关系的进程间的通信。  举例:父进程与子进程之间的通信。

  wKioL1cIprCDpLhMAAAprpxFmmw145.png

因为匿名管道提供的进程通信是单向的,所以去掉上图中子的读(写),去掉父的写(读)。就可以实现子写父读(子读父写)。

代码实现子写父读:

  
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<sys/types.h>
  6
  7 int main()
  8 {
  9   //1.创建管道
 10   int pipe_fd[2]={-1,-1};
 11   if(pipe(pipe_fd)<0)
 12   {
 13     perror("pipe");
 14     exit(1);
 15 
 16    }
 17  // printf("0-> %d,1-> %d",pipe_fd[0],pipe_fd[1]);检查读写
 18  //2.创建进程
 19  pid_t pid=fork();
 20  if(pid<0)
 21   {
 22     perror("fork");
 23     exit(2);
 24 
 25   }
 26 else if(pid==0)
 27 {
 28   //子进程
 29   close(pipe_fd[0]);
 30   int count=10;
 31   char buf[]="hello bit";
 32   while(count)
 33    {
 34       write(pipe_fd[1],buf, strlen(buf));
 35       count--;
 36       sleep(1);
 37    }
 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)-1);
 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 
 53         }
 54      }
 55     }
 56    close(pipe_fd[0]);
 57    return 0;
 58  }             
                                                                1,7           Top

程序执行结果:


wKioL1cIwSzzyZ4UAAAk7gmN5Kc057.png

父进程读取到子写的10条信息。

使用匿名管道注意的状况(假设都是阻塞I/O)

1.管道引用计数

 定义一个count,来决定读多少写多少

2.写端不关闭,并且不写,读端读完,继续等待,此时阻塞

3.读端关闭,写端在写,那么写进程收到信号SIGPIPE,通常导致进程异常中止。

4.读端没有关闭,但不读数据,写端一直在写,直到写满,再次阻塞或为空再写。

匿名管道的特点总结:

1.进程之间的通信是单向的

2.用于父子进程和有血缘关系的进程

3.提供的是流式服务

4.管道内部处理了数据写齐备然后才能读的机制

5.生命周期随进程。

三.命名管道(name pipe 或 FIFO)

命名管道是在shell交互地建立的,可以解决不同进程之间的进程问题。

代码:

client:发消息的
 #include<unistd.h>
int main()
 {

 umask(0);
//创建命名管道
  if(mkfifo("./.tmp",S_IFIFO|0666)<0)
   {
     perror("mkfifo");
     exit(1);
   }

  int fd=open("./.tmp",O_WRONLY);//写方式打开
  if(fd<0)
   {
     perror("open");
     exit(2);
    }
   char buf[1024];

    while(1)
     {
        memset(buf,'\0',sizeof(buf));
        fflush(stdout);
        ssize_t size=read(0,buf,sizeof(buf));//从标准入上读
        buf[size]='\0';
        write(fd,buf,sizeof(buf));
      }
     close(fd);
     return 0;
}
  
  server:接消息的
  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/stat.h>
  4 #include<fcntl.h>
  5 #include<stdlib.h>
  6 #include<string.h>
  7 #include<unistd.h>
  8 int main()
  9 {
 10 
 11   int fd=open("./.tmp",O_RDONLY);
 12   if(fd<0)
 13    {
 14      perror("open");
 15      exit(2);
 16     }
 17  char buf[1024];
 18  while(1)
 19  {
 20   ssize_t size=read(fd,buf,sizeof(buf));
 21   if(size>0)
 22   {
 23    buf[size-1]='\0';
  
 24    printf("client say %s\n",buf);
 25    }
 26  else
 27   {
 28    printf("client quit or error\n")   ;
 29    break;
 30 
 31    }
 32 
 33 
 34 }
 35  close(fd);
 36  return 0;
 37 }

运行结果:

client:

wKioL1cKMIKjPTWhAAAcUT84LUs827.png

server:

wKiom1cKMA-A92PqAAAWDphp238748.png