文章

Pipe笔记

Pipe笔记

2 管道(匿名)

2.1 管道的概念

  • 本质:
    • 内核缓冲区
    • 伪文件-不占用磁盘空间
  • 特点:
    • 两部分:
      • 读端,写端,对应两个文件描述符
      • 数据写端流入,读端流出
    • 操作管道的进程被销毁之后,管道自动被释放
    • 管道默认是阻塞的
      • 读写

2.2 管道的原理

  • 内部实现方式:队列
    • 环形队列
    • 特点:先进先出
  • 缓冲区大小:
    • 默认4K
    • 大小会根据实际情况做适当调整

2.3 管道的局限性

  • 队列
    • 数据只能读取一次,不能重复读取
  • 半双工:
    • 单工:遥控器
    • 半双工:对讲机
      • 数据传输的方向是单向的
    • 双工:电话
  • 匿名管道:
    • 适用于有血缘关系的进程

2.4 创建匿名管道

  • int pipe(int fd[2]);
    • fd - 传出参数
    • fd[0] - 读端(=3, 因为stdin=0, stdout=1, stderr=2)
    • fd[1] - 写端(=4)

2.5 父子进程使用管道通信

  • 思考:
    • 单个进程能否使用管道完成读写操作?
      • 可以
    • 父子进程间通信是否需要sleep函数?
  • 注意事项:
    • 父进程读时:关闭写端
    • 子进程写时:关闭读端
  • 练习
    1. 父子进程间通信,实现ps aux | grep bash
      • 数据重定向:dup2
      • execlp

      代码如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      
      /* 程序来源:https://www.bilibili.com/video/av17360025/?p=30
       * 程序功能:父子进程间通信,实现`ps aux | grep bash`
       */
           
      #include <stdio.h>
      #include <unistd.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <string.h>
           
      int main()
      {
      	int fd[2];
           
      	if(pipe(fd)){
      		perror("pipe error");
      		exit(EXIT_FAILURE);
      	}
           
      	pid_t pid=fork();
      	if(pid == -1){
      		perror("fork error");
      		exit(EXIT_FAILURE);
      	}
      	//父进程: `ps aux`
      	if(pid > 0){
      		// 写管道的操作,关闭读端
      		close(fd[0]);
      		// 文件描述符重定向:STDOUT_FILENO->管道写端
      		dup2(fd[1], STDOUT_FILENO);
      		execlp("ps", "ps", "aux", NULL);
      		perror("execlp");
      		exit(EXIT_FAILURE);
      	}
      	//子进程: `grep --color=auto bash`
      	else if(pid == 0){
      		close(fd[1]);
      		dup2(fd[0], STDIN_FILENO);
      		execlp("grep", "grep", "--color=auto", "bash", NULL);
      		perror("execlp");
      		exit(EXIT_FAILURE);
      	}
           
      	return 0;
      }
      
    2. 兄弟进程间通信,实现ps aux | grep bash。代码如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      
      /* 程序来源:https://www.bilibili.com/video/av17360025/?p=30
      * 程序功能:兄弟进程间通信,实现`ps aux | grep bash`
      */
      
      #include <stdio.h>
      #include <unistd.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <string.h>
      #include <wait.h>
      
      int main()
      {
        int fd[2];
      
        if(pipe(fd)){
          perror("pipe error");
          exit(EXIT_FAILURE);
        }
      
        int i;
        pid_t pid[2];
        for(i=0; i<2; i++){
          pid[i]=fork();
          if(pid[i] == -1){
            perror("fork error");
            exit(EXIT_FAILURE);
          }
          else if(pid[i] == 0) break;
        }
        //子进程1: `ps aux`
        if(i==0){
          // 写管道的操作,关闭读端
          close(fd[0]);
          // 文件描述符重定向:STDOUT_FILENO->管道写端
          dup2(fd[1], STDOUT_FILENO);
          execlp("ps", "ps", "aux", NULL);
          perror("execlp");
          exit(EXIT_FAILURE);
        }
        //子进程2: `grep --color=auto bash`
        else if(i == 1){
          close(fd[1]);
          dup2(fd[0], STDIN_FILENO);
          execlp("grep", "grep", "--color=auto", "bash", NULL);
          perror("execlp");
          exit(EXIT_FAILURE);
        }
        else if(i==2){
          close(fd[0]);
          close(fd[1]);
          pid_t wpid;
          while((wpid=waitpid(-1, NULL, WNOHANG))!=-1){
            if(wpid==0){
              continue;
            }
            printf("waitpid: %lu\n", wpid);
          }
        }
      
      
        return 0;
      }
      

2.6 管道的读写行为

  • 读操作
    • 有数据
    • 无数据
  • 写操作
    • 读端全部关闭
本文由作者按照 CC BY 4.0 进行授权