现在的位置: 首页 > 综合 > 正文

进程间通信——Interprocess communication

2018年04月01日 ⁄ 综合 ⁄ 共 5776字 ⁄ 字号 评论关闭

一、管道的特性  
  管道是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 }

抱歉!评论已关闭.