IPC之PIPE

  管道是一种只允许用在有亲属关系的进程间通信的方式,由函数pipe创建一个管道,read,write进行读写操作。

       #include <unistd.h>

       int pipe(int pipefd[2]);

 参数pipefd[2]数组返回打开的读写描述符,pipefd[0]为读,pipefd[1]为写。

  第一个问题:文件描述符怎么会出现一个只能读,一个只能写呢?猜想是对一个文件打开了2次,一个以只读打开,一个以只写打开。使用fcntl来验证下:

       #include <unistd.h>
       #include <fcntl.h>

       int fcntl(int fd, int cmd, ... /* arg */ );
       F_GETFL (void)
              Get the file access mode and the file status flags; arg is ignored.

cmd为F_GETFL时,最后一个参数arg被忽略。测试代码:

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <stdio.h>
 4 #include <fcntl.h>
 5 #include <signal.h>
 6 #include <unistd.h>
 7 #include <stdlib.h>
 8 
 9 int main(void)
10 {
11     int flags;
12     int fd[2];
13 
14     if (pipe(fd) < 0)
15     {
16         perror("pipe error");
17     }
18 
19     flags = fcntl(fd[0], F_GETFL,0);
20     if ( flags < 0 )
21     {
22         perror("fcntl");
23         close(fd[0]);
24         close(fd[1]);
25     }
26     switch (flags & O_ACCMODE) 
27     {
28         case O_RDONLY:
29             printf("read only
");
30             break;
31 
32         case O_WRONLY:
33             printf("write only
");
34             break;
35 
36         case O_RDWR:
37             printf("read write
");
38             break;
39 
40         default:
41             printf("unknown access mode
");
42     }
43 
44     flags = fcntl(fd[1], F_GETFL,0);
45     if ( flags < 0 )
46     {
47         perror("fcntl");
48         close(fd[0]);
49         close(fd[1]);
50     }
51     switch (flags & O_ACCMODE) 
52     {
53         case O_RDONLY:
54             printf("read only
");
55             break;
56 
57         case O_WRONLY:
58             printf("write only
");
59             break;
60 
61         case O_RDWR:
62             printf("read write
");
63             break;
64 
65         default:
66             printf("unknown access mode
");
67     }
68     close(fd[0]);
69     close(fd[1]);
70     exit(0);
71 }
View Code

运行结果:

read only
write only

与猜想相符。

  数据的流向:

从图中可以看出,进程可以以pipefd[1]写完,然后以pipefd[0]读,自己写自己读,这条数据流是通的。 验证:

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <stdio.h>
 4 #include <fcntl.h>
 5 #include <signal.h>
 6 #include <unistd.h>
 7 #include <stdlib.h>
 8 
 9 #define MAXLINE     4096
10 int main(void)
11 {
12     int flags;
13     int fd[2], n;
14     char buf[MAXLINE];
15     if (pipe(fd) < 0)
16     {
17         perror("pipe error");
18     }
19 
20     n = write(fd[1], "hello world
", MAXLINE);
21     if ( n < 0 )
22     {
23         perror("write");
24         goto end;
25     }
26     n = read(fd[0],buf, n);
27     if ( n < 0 )
28     {
29         perror("read");
30         goto end;
31     }
32     printf("read:%s
",buf);
33 
34 end:
35     close(fd[0]);
36     close(fd[1]);
37     exit(0);
38 }
View Code

输出:

read:hello world

  既然是进程间通信,那么管道在同一个进程中读写基本是没什么意义的,管道常用的方式是,先创建一个管道,然后fork,父子进程就共享了这个管道了。数据流向如图:

这样,管道的写端有2个进程操作,读端有2个进程操作。但是这样一来就出现了一个问题,假设父进程读,那么这个数据是它自己写进去的呢?还是子进程写进去的?无法区分。通常一个进程关闭它的读,另一个进程关闭它的写,这样,数据流向就只有一个方向了,数据来自谁就显而易见了。如图:

测试代码:

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <stdio.h>
 4 #include <fcntl.h>
 5 #include <signal.h>
 6 #include <unistd.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 
10 #define MAXLINE     4096
11 
12 int main(void)
13 {
14     int n;
15     int fd[2];
16     pid_t pid;
17     char line[MAXLINE];
18 
19     if (pipe(fd) < 0)
20         perror("pipe error");
21     if ((pid = fork()) < 0)
22     {
23         perror("fork error");
24     }
25     else if (pid > 0)          /* parent */
26     {
27         close(fd[0]);
28         write(fd[1], "hello world
", 12);
29     }
30     else                      /* child */
31     {
32         close(fd[1]);
33         n = read(fd[0], line, MAXLINE);
34         write(STDOUT_FILENO, line, n);
35     }
36     exit(0);
37 }
View Code

结果:

hello world

   读一个空的管道或者写一个满的管道都将导致阻塞,不过可以通过fcntlF_SETFL设置为O_NONBLOCK,从而不阻塞。

  当管道一端被关闭后,有下列2条规则:

  1.当读一个写端所有文件描述符引用都已被关闭的管道时,在所有数据被读完后,read将返回0。表示无数据可读。

  2.当写一个读端所有文件描述符引用都已被关闭的管道时,将产生SIGPIPE信号,write返回-1。

  

  混淆的东西,管道的容量管道的缓冲区大小

    管道的容量:指管道满时装的字节数,自2.6.11内核后,容量为64k。管道满了就会导致写操作产生阻塞。

    管道缓冲区大小:由PIPE_BUF指定,指的是保证管道写操作为原子操作的最大值,如果一次写入的内容超过这个值,那么这次的写操作就不是原子的。什么意思呢?就是指,可能存在多个进程写同一个管道,如果一次写入的字节数大于缓冲区大小,则可能会出现A进程写入的内容中插入了B进程写入的内容。

  下面是出现这种情况的代码:

 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <stdio.h>
 4 #include <fcntl.h>
 5 #include <signal.h>
 6 #include <unistd.h>
 7 #include <stdlib.h>
 8 #include <string.h>
 9 
10 #define MAXLINE     4096+100
11 
12 int main(void)
13 {
14     int n;
15     int fd[2];
16     pid_t pid;
17     char line[MAXLINE];
18 
19     if (pipe(fd) < 0)
20     {
21         perror("pipe error");
22     }
23     
24     if ((pid = fork()) < 0)
25     {
26         perror("fork error");
27     }
28     else if (pid > 0)          /* parent */
29     {
30         close(fd[1]);
31         while ( 1 )
32         {
33             n = read(fd[0], line, MAXLINE);
34             write(STDOUT_FILENO, line, n);
35             write(STDOUT_FILENO, "


", 3);
36         }
37     }
38     else                      /* child */
39     {
40         if ((pid = fork()) < 0)
41         {
42             perror("fork error");
43         }
44         else if (pid > 0)
45         {
46             close(fd[0]);
47             
48             while (1)
49             {
50                 memset(line, 'a',MAXLINE);
51                 write(fd[1], line, MAXLINE);
52             }
53         }
54         else
55         {
56             close(fd[0]);
57             
58             while ( 1 )
59             {
60                 memset(line, 'b',MAXLINE);
61                 write(fd[1], line, MAXLINE);
62             }
63         }
64     }
65     
66     exit(0);
67 }
View Code
原文地址:https://www.cnblogs.com/thammer/p/5093330.html