47.1 协同进程
47.1.1 介绍
两个进程通过两个管道进行双向通信称为协同进程。
47.1.2 例子
co_process.c
1 #include <sys/wait.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 int main(void) 8 { 9 int fda[2], fdb[2]; 10 11 /** 创建两个管道 */ 12 if((pipe(fda) < 0) || (pipe(fdb) < 0)){ 13 perror("pipe error"); 14 exit(1); 15 } 16 17 pid_t pid; 18 pid = fork(); 19 if(pid < 0){ 20 perror("fork error"); 21 } 22 else if(pid == 0){ 23 /** 子进程负责从管道 a 中读取父进程写入的累加参数 x 和 y 24 * 通过 exec 函数去调用 bin/add 程序进行累加 25 * 将累加结果写入管道 b */ 26 close(fda[1]); 27 close(fdb[0]); 28 29 /** 将标准输入重定向到管道 a 的读端 */ 30 /** add 将从管道 a 中读取累加参数 x 和 y */ 31 if(dup2(fda[0], STDIN_FILENO) != STDIN_FILENO){ 32 perror("dup2 error"); 33 } 34 if(dup2(fdb[1], STDOUT_FILENO) != STDOUT_FILENO){ 35 perror("dup2 error"); 36 } 37 38 if(execlp("bin/add", "bin/add", NULL) < 0){ 39 perror("execlp error"); 40 exit(1); 41 } 42 43 close(fda[0]); 44 close(fdb[1]); 45 } 46 else { 47 /** 从标准输入上读取累加参数 x 和 y 48 * 将 x 和 y 写入管道 a 49 * 从管道 b 中读取累加的结果并输出 */ 50 close(fda[0]); 51 close(fdb[1]); 52 int x,y; 53 printf("Please input x and y:"); 54 scanf("%d %d", &x, &y); 55 56 if(write(fda[1], &x, sizeof(int)) != sizeof(int)) { 57 perror("write error"); 58 } 59 if(write(fda[1], &y, sizeof(int)) != sizeof(int)) { 60 perror("write error"); 61 } 62 63 int result = 0; 64 if(read(fdb[0], &result, sizeof(int)) != sizeof(int)){ 65 perror("read error"); 66 } 67 else { 68 printf("add result is %d ", result); 69 } 70 71 close(fda[1]); 72 close(fdb[0]); 73 wait(0); 74 } 75 76 return 0; 77 }
add.c
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 6 int main(void) 7 { 8 int x, y; 9 10 if(read(STDIN_FILENO, &x, sizeof(int)) < 0) { 11 perror("read error"); 12 } 13 14 if(read(STDIN_FILENO, &y, sizeof(int)) < 0) { 15 perror("read error"); 16 } 17 18 int result = x + y; 19 20 if(write(STDOUT_FILENO, &result, sizeof(int)) != sizeof(int)){ 21 perror("write error"); 22 } 23 24 exit(0); 25 }
先编译 add.c ,再编译 co_process.c,运行结果如下:
47.2 读写特性
47.2.1 介绍
- 通过打开两个管道来创建一个双向管道
- 管道是阻塞性的,当进程从管道中读取数据,若没有数据,进程会阻塞
- 当一个进程往管道中不断地写入数据,但是没有进程去读取数据,此时只要管道是没有满是可以的,但若管道放满数据的,则会报错
- 不完整管道:
- 当读一个写端已经被关闭的管道时,在所有数据被读取后,read 返回 0,以表示到达了文件尾部。
- 如果写一个读端已被关闭的管道,则产生信号 SIGPIPE,如果忽略该信号或捕捉该信号并从处理程序返回,则 write 返回 -1,同时 errno 设置为 EPIPE
47.2.2 例子
例子1,读取写端关闭的不完整管道
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/wait.h> 7 8 /** 不完整管道:读取一个写端已经关闭的管道 */ 9 int main(void) 10 { 11 int fd[2]; 12 13 if(pipe(fd) < 0){ 14 perror("pipe error"); 15 exit(1); 16 } 17 18 pid_t pid; 19 if((pid = fork()) < 0){ 20 perror("fork error"); 21 exit(1); 22 } 23 else if(pid > 0){ 24 /** 父进程从不完整管道(写端关闭)中读取数据 */ 25 sleep(5); ///< 等子进程将管道的写端关闭 26 close(fd[1]); 27 while(1){ 28 char c; 29 if(read(fd[0], &c, 1) == 0){ 30 printf(" write-end of pipe closed "); 31 break; 32 } 33 else { 34 printf("%c", c); 35 } 36 } 37 38 close(fd[0]); 39 wait(0); 40 } 41 else { 42 /** 子进程负责将数据写入管道 */ 43 close(fd[0]); 44 char *s = "1234"; 45 write(fd[1], s, sizeof(s)); 46 /** 写入数据后关闭管道的写端 */ 47 close(fd[1]); 48 } 49 50 return 0; 51 }
编译运行结果如下:
例子2,写一个写端被关闭的不完整管道
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/wait.h> 7 #include <errno.h> 8 9 /** 不完整管道: 写一个写端已经关闭的管道 */ 10 11 void sig_handler(int signo) 12 { 13 if(signo == SIGPIPE){ 14 printf("SIGPIPE occured "); 15 } 16 } 17 18 int main(void) 19 { 20 int fd[2]; 21 22 if(pipe(fd) < 0){ 23 perror("pipe error"); 24 exit(1); 25 } 26 27 pid_t pid; 28 if((pid = fork()) < 0){ 29 perror("fork error"); 30 exit(1); 31 } 32 else if(pid > 0){ 33 /** 父进程负责将数据写如到不完整管道(读端关闭)中 */ 34 sleep(5); ///< 等子进程将管道的读端关闭 35 close(fd[0]); 36 if(signal(SIGPIPE, sig_handler) == SIG_ERR){ 37 perror("signal sigpipe error"); 38 exit(1); 39 } 40 41 char *s = "1234"; 42 if(write(fd[1], s, sizeof(s)) != sizeof(s)){ 43 fprintf(stderr, "%s, %s ", strerror(errno), 44 (errno == EPIPE) ? "EPIPE" : ", unknown"); 45 46 } 47 close(fd[1]); 48 wait(0); 49 } 50 else { 51 /** 子进程关闭管道的读端 */ 52 close(fd[0]); 53 close(fd[1]); 54 } 55 56 return 0; 57 }
编译运行: