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 }

 

 

你可能感兴趣的:(linux)