• 10.3 进程间通信--管道


    管道基本概念:

      管道是Unix中最古老的进程间通信形式

      我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

    管道的本质:固定大小的内核缓冲区

    管道限制:

      管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

      只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信,通常,一个管道由一个进程创建,然后该进程调用fork,此后,父子进程之间就可以应用该管道。

    匿名管道pipe:用在父子进程之间或有血缘关系的进程之间

      原型:int pipe(int  fd[2])

      功能:创建一个无名管道

      参数:

        fd:文件描述符数组,其中,fd[0]表示读端,fd[1]表示写端

      返回值:成功返回0,失败返回错误码

    管道创建后示意图:

    管道的读写规则如下:

    管道读写示例:

      1 #include <unistd.h>
      2 #include <sys/stat.h>
      3 #include <sys/wait.h>
      4 #include <sys/types.h>
      5 #include <fcntl.h>
      6 
      7 #include <stdlib.h>
      8 #include <stdio.h>
      9 #include <errno.h>
     10 #include <string.h>
     11 #include <signal.h>
     12 
     13 #define ERR_EXIT(m) 
     14     do 
     15     { 
     16         perror(m); 
     17         exit(EXIT_FAILURE); 
     18     } while(0)
     19 
     20 
     21 //测试 默认情况下,管道的读写
     22 int main41(void )
     23 {
     24     int pipefd[2];
     25     pid_t pid;
     26     if (pipe(pipefd) == -1 )    
     27     {
     28         printf("pipe() err..
    ");    
     29         return -1;
     30     }
     31     pid = fork();
     32     if (pid == -1)
     33     {
     34         printf("fork err..
    ");
     35         return -1;
     36     }
     37     
     38     if (pid == 0)
     39     {
     40         printf("子进程延迟3秒后,再写。。
    ");
     41         sleep(3);
     42         close(pipefd[0]);
     43         write(pipefd[1], "hello hello....", 6);
     44         close(pipefd[1]);
     45         printf("child .....quit
    ");
     46         exit(0);
     47     } 
     48     else if (pid > 0 )
     49     {
     50         int len = 0; 
     51         char buf[100] = {0};
     52         close(pipefd[1]);
     53     
     54     
     55         printf("测试非阻塞情况下,begin read ...
    ");
     56         len = -1;
     57         while (len < 0)
     58         {
     59                 len = read(pipefd[0], buf, 100);
     60                 if (len == -1)
     61                 {
     62                     sleep(1);
     63                     //close(pipefd[0]);
     64                     perror("
    read err:");
     65                     //exit(0);
     66                 }
     67         }
     68     
     69         printf("len:%d, buf:%s 
    ", len , buf);
     70         close(pipefd[0]);
     71     }
     72 
     73     wait(NULL);
     74     printf("parent ..quit
    ");
     75     return 0;
     76 
     77 }
     78 
     79 
     80 //文件状态设置成阻塞非阻塞
     81 int main(void )
     82 {
     83     int pipefd[2];
     84     pid_t pid;
     85     if (pipe(pipefd) == -1 )    
     86     {
     87         printf("pipe() err..
    ");    
     88         return -1;
     89     }
     90     pid = fork();
     91     if (pid == -1)
     92     {
     93         printf("fork err..
    ");
     94         return -1;
     95     }
     96     
     97     if (pid == 0)
     98     {
     99         sleep(3);
    100         close(pipefd[0]);
    101         write(pipefd[1], "hello hello....", 6);
    102         close(pipefd[1]);
    103         printf("child .....quit
    ");
    104     } 
    105     else if (pid > 0 )
    106     {
    107         int len = 0; 
    108         char buf[100] = {0};
    109         close(pipefd[1]);
    110     
    111     /*    
    112        int fcntl(int fd, int cmd);
    113        int fcntl(int fd, int cmd, long arg);
    114        int fcntl(int fd, int cmd, struct flock *lock);
    115 F_GETFL  F_SETFL O_NONBLOCK
    116 //注意不要设置错误成F_GETFD  F_SETFD
    117 
    118 
    119 */
    120         int flags = fcntl(pipefd[0], F_GETFL);
    121         flags = flags | O_NONBLOCK;
    122         int ret = fcntl(pipefd[0], F_SETFL, flags);
    123         if (ret == -1)
    124         {
    125             printf("fcntl err.
    ");
    126             exit(0);
    127         }
    128         
    129         //把pipefd[0]文件描述符修改成非阻塞 man fcntl
    130         printf("begin read ...
    ");
    131         len = -1;
    132         while (len < 0)
    133         {
    134                 len = read(pipefd[0], buf, 100);
    135                 if (len == -1)
    136                 {
    137                     sleep(1);
    138                     //close(pipefd[0]);
    139                     perror("read err.
    ");
    140                     //exit(0);
    141                 }
    142         }
    143     
    144         printf("len:%d, buf:%s 
    ", len , buf);
    145         close(pipefd[0]);
    146     }
    147 
    148     wait(NULL);
    149     printf("parent ..quit
    ");
    150     return 0;
    151 
    152 }

     测试管道容量的程序:2.6.11内核之后默认是65536字节

     1 #include <unistd.h>
     2 #include <sys/stat.h>
     3 #include <sys/wait.h>
     4 #include <sys/types.h>
     5 #include <fcntl.h>
     6 
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <errno.h>
    10 #include <string.h>
    11 #include <signal.h>
    12 //测试管道容量
    13 
    14 #define ERR_EXIT(m) 
    15     do 
    16     { 
    17         perror(m); 
    18         exit(EXIT_FAILURE); 
    19     } while(0)
    20 
    21 
    22 void myhandle(int sig)
    23 {
    24     printf("recv sig:%d 
    ", sig);
    25 }
    26 
    27 
    28 int main(void )
    29 {
    30     int pipefd[2];
    31     pid_t pid;
    32     
    33     //注册管道处理函数
    34     signal(SIGPIPE, myhandle);
    35     
    36     if (pipe(pipefd) == -1 )    
    37     {
    38         printf("pipe() err..
    ");    
    39         return -1;
    40     }
    41 
    42     pid = fork();
    43     if (pid == -1)
    44     {
    45         printf("fork err..
    ");
    46         return -1;
    47     }
    48     
    49     if (pid == 0)
    50     {
    51         int count = 0;
    52         int ret = 0;
    53         close(pipefd[0]);
    54         
    55         
    56         //写端变成非阻塞
    57         int flags = fcntl(pipefd[1], F_GETFL);
    58         flags = flags | O_NONBLOCK;
    59         ret = fcntl(pipefd[1], F_SETFL, flags);
    60         if (ret == -1)
    61         {
    62             printf("fcntl err.
    ");
    63             exit(0);
    64         }
    65         
    66         while(1)
    67         {
    68             ret = write(pipefd[1] , "a", 1);    
    69             if (ret == -1)
    70             {
    71                 perror("write pipe");
    72                 break;
    73             }
    74             count ++;    
    75         }
    76         
    77         printf("count:%d 
    
    ", count);
    78         close(pipefd[1]);
    79         
    80         exit(0);
    81     } 
    82     else if (pid > 0 )
    83     {
    84     
    85         sleep(4);
    86         close(pipefd[0]);
    87         close(pipefd[1]);
    88     }
    89 
    90     wait(NULL);
    91     printf("parent ..quit
    ");
    92     return 0;
    93 
    94 }

    测试管道写入的PIPE_BUF原子性:原理是两个进程向管道里面写,一个进程读,看读出的数据是否乱序

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <unistd.h>
     5 #include <sys/types.h>
     6 #include <errno.h>
     7 #include <fcntl.h>
     8 
     9 
    10 #define ERR_EXIT(m) 
    11         do 
    12         { 
    13                 perror(m); 
    14                 exit(EXIT_FAILURE); 
    15         } while(0)
    16 
    17 #define TEST_SIZE 68*1024   //68K
    18 
    19 
    20 int main(void)
    21 {
    22     char a[TEST_SIZE];
    23     char b[TEST_SIZE];
    24 
    25     memset(a, 'A', sizeof(a));
    26     memset(b, 'B', sizeof(b));
    27 
    28     int pipefd[2];
    29 
    30     int ret = pipe(pipefd);
    31     if (ret == -1)
    32         ERR_EXIT("pipe error");
    33 
    34     pid_t pid;
    35     pid = fork();
    36     if (pid == 0) //A子进程写68K数据A
    37     {
    38         close(pipefd[0]);
    39         ret = write(pipefd[1], a, sizeof(a));
    40         printf("apid=%d write %d bytes to pipe
    ", getpid(), ret);
    41         exit(0);
    42     }
    43 
    44     pid = fork();
    45 
    46     
    47     if (pid == 0) //B子进程写68K数据B
    48     {
    49         close(pipefd[0]);
    50         ret = write(pipefd[1], b, sizeof(b));
    51         printf("bpid=%d write %d bytes to pipe
    ", getpid(), ret);
    52         exit(0);
    53     }
    54 
    55 
    56     close(pipefd[1]);
    57     
    58     sleep(1);
    59     int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    60     char buf[1024*4] = {0};
    61     int n = 1;
    62     while (1)
    63     {
    64         //父进程4k 4k的读数据,发现AB进程是交叉的写数据到管道。
    65         //多个进程往管道里面,写数据。
    66         ret = read(pipefd[0], buf, sizeof(buf)); 
    67         if (ret == 0)
    68             break;
    69         printf("n=%02d pid=%d read %d bytes from pipe buf[4095]=%c
    ", n++, getpid(), ret, buf[4095]);
    70         write(fd, buf, ret);
    71 
    72     }
    73     return 0;    
    74 }

    自己实现管道:

     1 #include <unistd.h>
     2 #include <sys/stat.h>
     3 #include <sys/wait.h>
     4 #include <sys/types.h>
     5 #include <fcntl.h>
     6 
     7 #include <stdlib.h>
     8 #include <stdio.h>
     9 #include <errno.h>
    10 #include <string.h>
    11 #include <signal.h>
    12 
    13 #define ERR_EXIT(m) 
    14     do 
    15     { 
    16         perror(m); 
    17         exit(EXIT_FAILURE); 
    18     } while(0)
    19 
    20 int main21(void )
    21 {
    22     int pipefd[2];
    23     pid_t pid;
    24     if (pipe(pipefd) == -1 )    
    25     {
    26         printf("pipe() err..
    ");    
    27         return -1;
    28     }
    29     pid = fork();
    30     if (pid == -1)
    31     {
    32         printf("fork err..
    ");
    33         return -1;
    34     }
    35     if (pid == 0)
    36     {
    37         close(pipefd[0]);
    38         //复制文件描述符pipefd[1],给标准输出,言外之意:execlp的ls命令输出到管道中
    39         dup2(pipefd[1], STDOUT_FILENO);
    40         close(pipefd[1]);
    41         
    42         execlp("ls", "ls", NULL);
    43         //如果替换新的进程印象失败,则会执行下面一句话    
    44         sprintf(stderr, "execute the cmd ls err..
    ");
    45         exit(0);    
    46     
    47     
    48     } 
    49     else if (pid > 0 )
    50     {
    51         int len = 0; 
    52         char buf[100] = {0};
    53         close(pipefd[1]);
    54         //复制文件描述符pipefd[0],给标准输入,言外之意:execlp的wc命令从管道中读
    55         dup2(pipefd[0], STDIN_FILENO);
    56         close(pipefd[0]);
    57         //len = read(pipefd[0], buf, 100);
    58         execlp("wc", "wc", "-w", NULL);
    59         printf("len:%d, buf:%s 
    ", len , buf);
    60 
    61         //close(pipefd[0]);
    62     }
    63 
    64     wait(NULL);
    65     printf("parent ..quit
    ");
    66     return 0;
    67 
    68 }
    69 
    70 
    71 int main(int argc, char *argv[])
    72 {
    73     close(0); //关闭标准输入
    74     open("makefile", O_RDONLY); //makefile文件变成标准输入
    75     close(1);//关闭标准输出
    76     open("makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644); //maifle2变成标准输出
    77 
    78     execlp("cat", "cat", NULL); //替换进程印象后,执行cat命令
    79     
    80     //cat命令 从标准输入中按行读,紧接着写到标准输出
    81 
    82     return 0;
    83     
    84 }

     改变文件系统描述符实现文件复制:

    命名管道FIFO:可以用在不相关的进程之间

     

    匿名管道由pipe函数创建和打开,而命名管道由mkfifo创建,由open打开,相当于创建和打开时分开的。

    示例如下:

    写管道:

    读管道:

  • 相关阅读:
    时间等待太重要!!!
    (引用 )自动化测试报告HTMLtestrunner
    (转载)selenium-webdriver(python)
    lr_convert_string_encoding()转码函数
    分步骤学习自动化测试
    (引用)web安全测试
    Monkey测试
    (学习网址)Python 自动化测试
    (引用) unittest测试驱动之执行测试(三)
    log4net日志组件
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9426865.html
Copyright © 2020-2023  润新知