zoukankan      html  css  js  c++  java
  • 四十七、进程间通信——管道的协同进程和读写特性

    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 }

      编译运行:

      

  • 相关阅读:
    docker search 报错
    mgo连接池
    饿了么这样跳过Redis Cluster遇到的“坑”
    Linux Swap的那些事
    epoll使用详解(精髓)(转)
    select、poll、epoll之间的区别总结[整理](转)
    git merge 和 git rebase 小结(转)
    linux查看端口占用情况
    [LeetCode] Combinations——递归
    C++中的static关键字的总结(转)
  • 原文地址:https://www.cnblogs.com/kele-dad/p/10284818.html
Copyright © 2011-2022 走看看