• 四十六、进程间通信——管道的分类与读写


    46.1 管道介绍

    46.1.1 管道通信

    • 管道是针对于本地计算机的两个进程之间的通信而设计的通信方法,建立管道后,实际获得两个文件描述符:一个用于读取而另一个用于写入
    • 最常见的 IPC 机制,通过 pipe 系统调用
    • 管道是单工的,数据只能向一个方向流动,需要双向通信时,需要建立起两个管道
    • 数据的读出和写入:
      • 一个进程向管道中写的内容被管道另一端的进程读出。
      • 写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据
    • 管道实际上就是建立在内核区域的一块缓存

    46.1.2 管道分类

    • 匿名管道:
      • 在关系进程中进行(父进程和子进程、兄弟进程之间)
      • 由 pipe 系统调用,管道由父进程建立
      • 管道位于内核空间,其实是一块缓存
    • 命名管道(FIFO)
      • 两个没有任何关系的进程之间通信可通过命名管道进行数据传输,本质是内核中的一块缓存,另在文件系统中以一个特殊的设备文件(管道文件)存在
      • 通过系统调用 mkfifo 创建

    46.1.3 管道创建

    1 #include <unistd.h>
    2 int pipe(int fd[2]);
    • 函数参数:两个文件描述符数组
      • fd[0]:为 pipe 的读端
      • fd[1]:为 pipe 的写端
      • fd[0] 用于读取管道,fd[1] 用于写入管道
    • 返回值:成功返回 0;出错返回 -1

      

      案例

      

    46.1.4 管道读写

    • 管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过 fork 函数创建一个子进程

      

    46.2 例子

    46.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  *  父进程通过管道传输两个数据给子进程
    10  *  由子进程负责从管道中读取并输出
    11  */
    12 int main(void)
    13 {
    14     int fd[2];
    15 
    16     /** 创建管道 */
    17     if(pipe(fd) < 0){
    18         perror("pipe error");
    19         exit(1);
    20     }
    21 
    22     pid_t pid;
    23     if((pid = fork()) < 0){
    24         perror("fork error");
    25         exit(1);
    26     }
    27     else if(pid > 0) { ///< 父进程
    28         close(fd[0]);    ///< 父进程用来写入数据
    29         int start = 1, end = 100;
    30         if((write(fd[1], &start, sizeof(int))) != sizeof(int)) {
    31             perror("write error");
    32             exit(1);
    33         }
    34 
    35         if((write(fd[1], &end, sizeof(int))) != sizeof(int)) {
    36             perror("write error");
    37             exit(1);
    38         }
    39 
    40         close(fd[1]);
    41         wait(0);
    42     }
    43     else {  ///< 子进程
    44         close(fd[1]);   ///< 子进程用来读取数据
    45         int start, end;
    46         if(read(fd[0], &start, sizeof(int)) < 0){
    47             perror("read error");
    48             exit(1);
    49         }
    50 
    51         if(read(fd[0], &end, sizeof(int)) < 0){
    52             perror("read error");
    53             exit(1);
    54         }
    55 
    56         close(fd[0]);
    57         printf("child process read start: %d, end %d
    ", start, end);
    58     }
    59 
    60     return 0;
    61 }

      编译运行:

      

     46.2.2 在管道系统中使用 excu 函数

      

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/wait.h>
     5 #include <sys/types.h>
     6 
     7 char *cmd1[3] = {"/bin/cat", "/etc/passwd", NULL};
     8 char *cmd2[3] = {"/bin/grep", "root", NULL};
     9 
    10 int main(void)
    11 {
    12     int fd[2];
    13     if(pipe(fd) < 0){
    14         perror("pipe error");
    15         exit(1);
    16     }
    17 
    18     int i = 0;
    19     pid_t pid;
    20     for(; i < 2; i++){
    21         pid = fork();
    22         if(pid < 0){
    23             perror("fork error");
    24             exit(1);
    25         }
    26         else if(pid == 0){
    27             if(i == 0){ /** 第一个子进程负责往管道写入数据 */
    28                 /** 关闭读端 */
    29                 close(fd[0]);
    30 
    31                 /** 将标准输出重定向到管道的写端
    32                  * 下面命令的执行的结果会写入到管道中,而不是输出到屏幕 */
    33                 if(dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
    34                 {
    35                     perror("dup2 error");
    36                 }
    37 
    38                 close(fd[1]);
    39 
    40                 /** 调用 exec 函数执行 cat 命令 */
    41                 if(execvp(cmd1[0], cmd1) < 0){
    42                     perror("execvp error");
    43                     exit(1);
    44                 }
    45                 break;
    46             }
    47 
    48             if(i == 1){/** 第二个子进程负责从管道读取数据 */
    49                 /** 关闭写端 */
    50                 close(fd[1]);
    51 
    52                 /** 将标准输入重定向到管道的读端 */
    53                 if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO){
    54                     perror("dup2 error");
    55                 }
    56 
    57                 close(fd[0]);
    58 
    59                 /** 调用 exec 函数执行 grep 命令 */
    60                 if(execvp(cmd2[0], cmd2) < 0){
    61                     perror("execvp error");
    62                     exit(1);
    63                 }
    64 
    65                 break;
    66             }
    67         }
    68         else {
    69             if(i == 1){
    70                 /** 父进程要等到子进程全部创建完毕才去回收 */
    71                 close(fd[0]);
    72                 close(fd[1]);
    73                 wait(0);
    74                 wait(0);
    75             }
    76 
    77         }
    78     }
    79     return 0;
    80 }

      执行结果如下:

      

  • 相关阅读:
    2017/09/02笔记:ps
    207/08/3学习笔记:pc端网站如何实现移动端适配知识点
    2017/0828xueix笔记:图像替代文本&css绘制的图形
    20170824:面试题笔记
    目前比较全的CSS重设(reset)方法总结
    学习笔记:css3实现多行文字溢出显示省略号&display:box;
    SVG圆形<circle> 标签
    k8s节点分配nodeSelector, Affinity和Anti-Affinity 亲和性和反亲和性
    kubernetes网络介绍
    centos6.7 安装docker
  • 原文地址:https://www.cnblogs.com/kele-dad/p/10279756.html
Copyright © 2020-2023  润新知