linux进程间通信之管道(APUE)

一、管道的特性  

  管道是UNIX系统IPC的最古老形式,并且所有UNIX系统都提供此种通信机制。但是管道有以下两种局限性:

  (1)半双工的(即数据只能在一个方向上流动),虽然有系统提供全双工的管道,但是为了可移植性,用户不能假设这种情况;

  (2)管道只能在具有公共祖先的进程之间使用。

二、管道的创建

管道由pipe函数创建:

 

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

 

函数返回值:若成功则返回0,若出错返回-1。

经由参数filedes返回两个文件描述符,filedes[0]为读而打开,filedes[1]为写而打开,也就是说filedes[1]的输出作为filedes[0]的输入

三、管道的使用

  单个进程的管道几乎没有用处,一般情况下,用户创建父子两个进程,即父进程创建pipe后接着调用 fork()函数,这样就创建了父进程到子进程(或者反向)的管道。下面考虑父进程到子进程的管道:fork之后父进程所有打开的文件描述符都被复制给子进程,也就是说,父子进程每个打开的相同描述符共享一个文件表项,父进程关闭管道的读端(filedes[0]),子进程关闭管道的写端(filedes[1]),这样,对于管道,父进程负责写数据,子进程负责读数据。

当管道的一端被关闭后,下列两条规则起作用:

  (1)当读一个写端已经关闭的管道,在所有数据都被读取后,read返回0,以表示达到文件末尾;

  (2)当写一个读端已经关闭的管道,则产生信号SIGPIPE。

  根据APUE,借用系统已经存在的分页程序(如more或者less),通过管道直接将文件内容送到分页程序,代码如下:

 1 #include <stdlib.h>
 2 #include <unistd.h>
 3 #include <sys/wait.h>
 4 #include <string.h>
 5 
 6 #define MAXLINE 1024
 7 #define DEF_PAGER "/bin/more"
 8 
 9 int main(int argc, char *argv[])
10 {
11     int count;
12     int fd[2];
13     pid_t pid;
14     char line[MAXLINE];
15     char *pager, *argv0;
16     FILE *fp;
17 
18     if((fp = fopen(argv[1], "r")) == NULL)
19       perror("fopen");
20 
21     if(pipe(fd) < 0)
22       perror("pipe");
23 
24     if((pid = fork()) < 0)
25       perror("fork");
26     else if(pid > 0) {
27         close(fd[0]);
28         while(fgets(line, MAXLINE, fp) != NULL) {
29             count = strlen(line);
30             write(fd[1], line, count);
31         }
32         if(ferror(fp))
33           perror("fgets");
34         close(fd[1]);
35         if(waitpid(pid, NULL, 0) < 0)
36           perror("waitpid");
37         exit(0);
38     } else {
39         close(fd[1]);
40         /* 将管道复制为标准输入 */
41         if(fd[0] != STDIN_FILENO) {
42             if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
43               perror("dup2");
44             close(fd[0]);
45         }
46         if((pager = getenv("PAGER")) == NULL)
47           pager = DEF_PAGER;
48         if((argv0 = strrchr(pager, '/')) != NULL)
49             argv0++;
50         else
51           argv0 = pager;
52         if(execl(pager, argv0, NULL) < 0)
53           perror("execl");
54     }
55     exit(0);
56 }

在这个程序中,父进程创建管道后,调用fork产生子进程,父进程首先关闭读端,运用fgets将文件内容读入,并写入管道;子进程关闭写端,并将标准输入和fd[0]共享同一个文件表项,这样分页程序就能够利用管道中的数据。

注:dup2(filedes, filedes2)函数的作用是使得filedes和filedes2都指向filedes指向的文件表。一个比较特殊的例子:

 

1 int
2 main(void)
3 {
4     dup2(0, STDOUT_FILENO);
5     printf("hello\n");
6     return 0;
7 }

 

这里将STDOUT_FILENO dup到标准输入,而函数之所以有输出hello,是因为使用了/dev/pts或者/dev/tty*,而这两种设备的驱动程序在处理对它们的写入操作时,采用了连接显示器输出这样的机制(也就是回显机制),所以能看到终端显示的hello,验证如下:

1 elvis@elvis:~/program/test$ ./a.out 1>dup2.out 
2 hello
3 elvis@elvis:~/program/test$ ./a.out 0>dup2.out 
4 elvis@elvis:~/program/test$ 

 四、FIFO

FIFO为有名管道,和无名管道的不同之处在于,无名管道需要两个拥有共同祖先的进程通信,而FIFO并不要求这样,FIFO依然是半双工管道。创建FIFO的函数如下:

 

1 #include <sys/stat.h>
2 int mkfifo(const char *name, mode_t mode);

 

成功返回0,失败返回-1。

建立一个服务器端和多个客户端程序,客户端通过共知的服务器管道向服务器发送数据,服务器通过客户端进程PID建立多个私有管道,转发来自客户端的数据。

服务器端:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <fcntl.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <string.h>
 7 #include <sys/types.h>
 8 #include <sys/stat.h>
 9 #include <signal.h>
10 
11 #define FIFO_SERV "/tmp/serv"
12 #define bufSize 256
13 
14 static void rm_fifo(void);
15 
16 int
17 main(void)
18 {
19     int count;
20     int server_fd, client_fd;
21     pid_t pid;
22     char recv_buf[bufSize];
23     char send_buf[bufSize];
24     char *pstr;
25     struct sigaction action;
26 
27     action.sa_handler = SIG_IGN;
28     sigemptyset(&action.sa_mask);
29 
30     if(sigaction(SIGPIPE, &action, NULL) < 0) {
31         perror("sigaction failed");
32         exit(1);
33     }
34 
35     if(unlink(FIFO_SERV) == -1) {
36         if(errno != ENOENT)
37           perror("unlink failed");
38     }
39 
40     if(mkfifo(FIFO_SERV, S_IFIFO | 0777) == -1) {
41         perror("mkfifo failed");
42         exit(1);
43     }
44 
45     if(atexit(rm_fifo) == -1) {
46         perror("atexit");
47         exit(1);
48     }
49 
50     if((server_fd = open(FIFO_SERV, O_RDONLY)) == -1) {
51         perror("open server failed");
52         exit(1);
53     }
54 
55     memset(recv_buf, 0, bufSize);
56     while((count = read(server_fd, recv_buf, bufSize)) > 0) {
57         recv_buf[count] = '\n';
58         printf("%s\n", recv_buf);
59         
60         strcpy(send_buf, recv_buf);
61 
62         /* 
63         if(unlink(send_buf) == -1) {
64             if(errno != ENOENT) {
65                 perror("can't open file");
66                 exit(1);
67             }
68         }
69  */
70         if(mkfifo(send_buf, S_IFIFO | 0777) == -1) {
71             perror("mkfifo failed");
72             exit(1);
73         }
74 
75         if((client_fd = open(send_buf, O_WRONLY)) == -1) {
76             perror("open client failed");
77             exit(1);
78         }
79         
80         memset(send_buf, 0, bufSize);
81         pstr = recv_buf;
82         sprintf(send_buf, "serv2clie%s", pstr+10);
83         write(client_fd, send_buf, bufSize);
84         close(client_fd);
85         sleep(3);
86     }
87 
88     close(server_fd);
89     exit(0);
90 }
91 
92 static void
93 rm_fifo(void)
94 {
95     unlink(FIFO_SERV);
96 }

客户端:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <fcntl.h>
 4 #include <unistd.h>
 5 #include <errno.h>
 6 #include <string.h>
 7 #include <sys/types.h>
 8 #include <sys/stat.h>
 9 
10 #define FIFO_SERV "/tmp/serv"
11 #define bufSize 256
12 
13 static void rm_fifo(void);
14 
15 int
16 main(void)
17 {
18     int count;
19     int server_fd, client_fd;
20     pid_t pid;
21     char recv_buf[bufSize];
22     char send_buf[bufSize];
23 
24     if((server_fd = open(FIFO_SERV, O_WRONLY)) == -1) {
25         perror("open server failed");
26         exit(1);
27     }
28 
29     memset(send_buf, 0, bufSize);
30     sprintf(send_buf, "/tmp/clie_%d", getpid());
31 
32     write(server_fd, send_buf, bufSize);
33 
34     while((client_fd = open(send_buf, O_RDONLY)) == -1) {
35         if(errno != ENOENT) {
36             printf("can't open client fifo");
37             exit(1);
38         }
39         //否则,一直连续等待服务器创建
40     }
41 /* 
42     if(unlink(send_buf) == -1) {
43         perror("unlink failed");
44         exit(1);
45     }
46      */
47     read(client_fd, recv_buf, bufSize);
48     printf("%s\n", recv_buf);
49 
50     unlink(send_buf);
51 
52     close(server_fd);
53     close(client_fd);
54     exit(0);
55 }

 

 

posted @ 2012-09-09 21:35  leealways87  阅读(625)  评论(0编辑  收藏  举报