管道是一种只允许用在有亲属关系的进程间通信的方式,由函数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\n");
30 break;
31
32 case O_WRONLY:
33 printf("write only\n");
34 break;
35
36 case O_RDWR:
37 printf("read write\n");
38 break;
39
40 default:
41 printf("unknown access mode\n");
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\n");
55 break;
56
57 case O_WRONLY:
58 printf("write only\n");
59 break;
60
61 case O_RDWR:
62 printf("read write\n");
63 break;
64
65 default:
66 printf("unknown access mode\n");
67 }
68 close(fd[0]);
69 close(fd[1]);
70 exit(0);
71 }
运行结果:
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\n", 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\n",buf);
33
34 end:
35 close(fd[0]);
36 close(fd[1]);
37 exit(0);
38 }
输出:
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\n", 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 }
结果:
hello world
读一个空的管道或者写一个满的管道都将导致阻塞,不过可以通过fcntl的F_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, "\n\n\n", 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 }
来源:http://www.cnblogs.com/thammer/p/5093330.html
