zoukankan      html  css  js  c++  java
  • linux多进/线程编程(4)——进程间通信之pipe和fifo

     

    前言:

    Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。

    常用的进程间通信方式有:

    ① 管道 (使用最简单)

    ② 信号 (开销最小)

    ③ 共享映射区 (无血缘关系)

    ④ 本地套接字 (最稳定) https://zhuanlan.zhihu.com/p/336734605

    本文主要讲解pipe管道的用法和代码示例。

    管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
      1. 其本质是一个伪文件(实为内核缓冲区) 
      2. 由两个文件描述符引用,一个表示读端,一个表示写端。
      3. 规定数据从管道的写端流入管道,从读端流出。
    管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。

    管道的局限性:

    1. 数据一旦被读走,便不在管道中存在,不可反复读取。
    2. 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
    3. 只能在有公共祖先的进程间使用管道。

    pipe

    NAME
           pipe, pipe2 - create pipe
    
    SYNOPSIS
           #include <unistd.h>
    
           int pipe(int pipefd[2]);
    
           #define _GNU_SOURCE             /* See feature_test_macros(7) */
           #include <fcntl.h>              /* Obtain O_* constant definitions */
           #include <unistd.h>
    
           int pipe2(int pipefd[2], int flags);

     利用pipe实现ps aux | grep bash

    int pipe_fd[2];
      pipe(pipe_fd);
    
      pid_t pid = fork();
    
      if (pid == 0) {
        //son  -----> 这里会产生僵尸进程
        //管道的使用规范:关闭读端
        close(pipe_fd[0]);
        //1.先重定向
        dup2(pipe_fd[1], STDOUT_FILENO);//标准输出重定向到管道写端
        //2.execlp
        execlp("ps", "ps", "aux", nullptr);
      } else if (pid > 0) {
        //parent
        //管道的使用规范:关闭写端
        close(pipe_fd[1]);
        //1.先重定向
        dup2(pipe_fd[0], STDIN_FILENO);//标准输入重定向到管道读端
        //2.execlp   -----> grep 会阻塞
        execlp("grep", "grep", "--color=auto", "bash", nullptr);
        //代码的问题:父进程认为还有写端存在,就有可能还有人给发数据,继续等待
        //管道的使用规范
      }

    fifo 可以实现无血缘关系的进程间通信

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    
    
    int main(int argc, char * argv[]) {
      if (argc != 2) {
        printf("./a.out fifonname
    ");
        return -1;
      }
      //当前目录有一个myfifo文件 
      //打开fifo文件
      int fd = open(argv[1], O_WRONLY);
      //
      char buf[256];
      int num = 1;
      while(1) {
        memset(buf, 0x00, sizeof(buf));
        //循环写
        sprintf(buf, "xiaoming%04d", num++);
        write(fd, buf, strlen(buf));
        sleep(1);
      }
    
      //关闭描述符
      closde(fd);
    
      return 0;
    }
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    
    
    int main(int argc, char * argv[]) {
      if (argc != 2) {
        printf("./a.out fifonname
    ");
        return -1;
      }
      //当前目录有一个myfifo文件 
      //打开fifo文件
      int fd = open(argv[1], O_RDONLY);
    
      char buf[256];
      int ret = 0;
      while(1) {
        //循环读
        ret = read(fd, buf, sizeof(buf));
        if (ret > 0) {
          printf("read:%s
    ", buf);
        }
      }
    
      //关闭描述符
      closde(fd);
    
      return 0;
    }
  • 相关阅读:
    如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习)如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习)
    shell命令【ulimit】
    ARM开发经典学习网站推荐
    [转]链表逆序
    [转]Rhythmbox中文乱码解决办法
    vi/vim 查找替换使用方法
    [转]程序员的十个层次 你属于哪一层?
    如何在程序中删除一个文件
    C/C++编译器错误代码大全
    R制作eset 的简单步骤
  • 原文地址:https://www.cnblogs.com/kongweisi/p/14629046.html
Copyright © 2011-2022 走看看