管道pipe/fifo

1.管道的概念

  管道本质上是内核中的一个缓冲区,让具有血缘关系的进程之间可以进行通信。它其实相当于一个伪文件,不占用磁盘空间,我们可以通过和对文件一样的读写方式去操作管道。

  管道具有两部分,读端和写端,分别对应两个文件描述符,数据从写端流入,从读端流出。

  操作管道的进程被销毁之后,管道会被系统自动释放。

  管道读写的时候默认是阻塞的。

   

2.管道的原理

  管道的内部是通过循环队列实现的,先进先出。

  管道的大小即缓冲区的大小默认是4k,但会根据实际情况做适当调整。

 

3.管道的局限性

  由于管道的实现形式是队列,使用数据只能读取一次,不能重复读取。

  管道实现形式是半双工的,所以在一端写数据的时候,另一段只能读数据,不能写数据。

  匿名管道pipe只适合于有血缘关系的进程使用。

 

4.创建匿名管道

int pipe(int fd[2]);

  fd:传出参数

  fd[0]:读端

  fd[1]:写端

 

5.进程之间使用管道通信

  单个进程可以使用管道完成读写操作。

  一个进程在读数据的时候要关闭写端,同理,在写数据的时候要关闭读端。

例:父子进程间通信,实现ps aux | grep bash

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <fcntl.h>
 5 
 6 int main()
 7 {
 8         int fd[2];
 9         int ret = pipe(fd);
10         if(ret == -1){
11                 perror("pipe error:");
12                 exit(-1);
13         }
14 
15         pid_t pid = fork();
16         if(pid == -1){
17                 perror("fork error:");
18                 exit(-1);
19         }else if(pid > 0){
20                 close(fd[0]);
21                 dup2(fd[1],STDOUT_FILENO);
22                 execlp("ps","ps","aux",NULL);
23                 perror("execlp error:");
24                 exit(-1);
25         }else{
26                 close(fd[1]);
27                 dup2(fd[0],STDIN_FILENO);
28                 execlp("grep","grep","--color=auto","bash",NULL);
29                 perror("execlp error:");
30                 exit(-1);
31         }
32         return 0;
33 }

 

兄弟进程间通信,实现ps aux | grep bash

 1        int fd[2];
 2         int ret = pipe(fd);
 3         if(ret == -1){
 4                 perror("pipe error:");
 5                 exit(-1);
 6         }
 7 
 8         int i;
 9         int n = 2;
10         pid_t pid;
11         for(i=0;i<n;i++){
12                 pid = fork();
13                 if(pid == 0){
14                         break;
15                 }
16         }
17 
18         if(i == 0){
19                 close(fd[0]);
20                 dup2(fd[1],STDOUT_FILENO);
21                 execlp("ps","ps","aux",NULL);
22                 perror("execlp error:");
23                 exit(-1);
24         }else if(i == 1){
25                 close(fd[1]);
26                 dup2(fd[0],STDIN_FILENO);
27                 execlp("grep","grep","--color=auto","bash",NULL);
28                 perror("execlp error:");
29                 exit(-1);
30         }else{
31                 pid_t wpid;
32                 close(fd[0]);
33                 close(fd[1]);
34                 while((wpid = wait(NULL))!=-1){
35                         printf("child = %d\n",wpid);
36                 }
37                 printf("child are all died\n");
38         }
39         return 0;
40 }

 

6.管道的读写操作

读操作:

  有数据时:read(fd)正常读,返回读出的字节数。

  无数据时:①写端全部关闭,read接触阻塞,返回0(相当于读文件读到了尾部)

       ②没有全部关闭,read阻塞

写操作:

  读端全部关闭:写的内容超出缓冲区大小时,管道破裂,内核给当前进程发信号SIGPIPE,进程被终止。

  读端没有全部关闭:①缓冲区写满了:write阻塞

           ②缓冲区没有写满:write继续写

 

设置管道为非阻塞

  管道默认两端都为阻塞,由于fcntl函数可以修改文件属性,所以可通过fcntl函数设置管道为非阻塞。如设置读端为非阻塞:

设置方法:

  获取原来的flags:int flags = fcntl(fd[0],F_GETFL);

  设置新的flags:flags |= O_NONBLOCK;

         fcntl(fd[0],F_SETFL,flags);

7.查看管道缓冲区大小

命令:ulimit -a

函数:fpathconf

 

8.有名管道fifo

特点:

  ①有文件名

  ②在磁盘上有这样一个文件(通过ls -l查看  ->  p)

  ③伪文件,大小为0

  ④在内核中有一个对应的缓冲区

  ⑤半双工的通信方式

使用场景:

  没有血缘关系的进程间通信。

创建方式:

  命令:mkfifo 管道名

  函数:mkfifo

操作方式:与文件大致相同

  open/close

  read/write

  不能执行lseek操作

进程间通信:

读端:

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <fcntl.h>
 4 #include <unistd.h>
 5 #include <sys/types.h>
 6 #include <sys/stat.h>
 7 
 8 int main(int argc,char *argv[])
 9 {
10         if(argc < 2){
11                 printf("./a.out filename\n");
12                 exit(-1);
13         }
14 
15         int ret;
16         ret = access(argv[1],F_OK);
17         if(ret == -1){
18                 int r = mkfifo(argv[1],0664);
19                 if(r == -1){
20                         perror("mkfifo error:");
21                         exit(-1);
22                 }
23         }
24 
25         int fd = open(argv[1],O_RDONLY);
26         if(fd == -1){
27                 perror("open error:");
28                 exit(-1);
29         }
30         char buf[128];
31         while(1){
32                 ret = read(fd,buf,sizeof(buf));
33                 buf[ret] = 0;
34                 printf("%s\n",buf);
35         }
36         close(fd);
37         return 0;
38 }

 

写端:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <fcntl.h>
 4 #include <unistd.h>
 5 #include <string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 
 9 int main(int argc,char *argv[])
10 {
11         if(argc < 2){
12                 printf("./a.out filename\n");
13                 exit(-1);
14         }
15 
16         int ret;
17         ret = access(argv[1],F_OK);
18         if(ret == -1){
19                 int r = mkfifo(argv[1],0664);
20                 if(r == -1){
21                         perror("mkfifo error:");
22                         exit(-1);
23                 }
24         }
25         int fd = open(argv[1],O_WRONLY);
26         if(fd == -1){
27                 perror("open error:");
28                 exit(-1);
29         }
30 
31         char *buf = "hello,world";
32         while(1){
33                 sleep(1);
34                 ret = write(fd,buf,strlen(buf)+1);
35         }
36         close(fd);
37         return 0;
38 }

 

上一篇:javascript – 使用Node.js实时读取文件


下一篇:linux – 单独程序的多对一双向通信