Linux系统编程之匿名管道

1.进程间通信介绍

1.1 进程通信的基本概念

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

Linux系统编程之匿名管道

1.2 为什么要进程间通信

进程通信主要有以下目的:

  • 数据传输:一个进程需要将它的数据发送给另一个进程。
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

1.3 常见的进程通信方式

在进程间完成数据传递需要借助操作系统提供特殊的方法,如今常见的进程间通信方式有:

① 管道 (分为匿名管道与命名管道)
​ ② 信号 (开销最小)
​ ③ 共享内存

2.管道

2.1管道简介

管道是Unix中最古老的进程间通信方式,我们把从一个进程连接到另一个进程的数据流叫做管道。

在Linux中,| 符号被用来代表管道。因为在Linux中,不同的命令,如ps,ls,grep等命令的本质都是可执行程序,| 前面的命令前面的命令通常会输出大量的结果,这些结果将会交由 | 后面的命令继续处理。

如下面这个命令就是将ps axj中含有PID的结果输出:

Linux系统编程之匿名管道

2.2 管道的创建和应用

管道的本质是内核中一块供不同进程进行读写的缓冲区,而外在的操作形式是通过文件读写的方式进行。

#include <unistd.h>
&#x529F;&#x80FD;:&#x521B;&#x5EFA;&#x4E00;&#x65E0;&#x540D;&#x7BA1;&#x9053;
&#x539F;&#x578B;
int pipe(int fd[2]);
&#x53C2;&#x6570;
fd&#xFF1A;&#x6587;&#x4EF6;&#x63CF;&#x8FF0;&#x7B26;&#x6570;&#x7EC4;,&#x8FD9;&#x662F;&#x4E00;&#x4E2A;&#x8F93;&#x51FA;&#x578B;&#x53C2;&#x6570;&#xFF0C;&#x8C03;&#x7528;&#x8BE5;&#x63A5;&#x53E3;&#x540E;&#xFF0C;&#x5C06;&#x4F1A;&#x7ED9;fd[2]&#x6570;&#x7EC4;&#x5206;&#x914D;&#x4E24;&#x4E2A;&#x6587;&#x4EF6;&#x63CF;&#x8FF0;&#x7B26;&#xFF0C;&#x4E24;&#x4E2A;&#x6587;&#x4EF6;&#x63CF;&#x8FF0;&#x7B26;&#x5206;&#x522B;&#x5BF9;&#x5E94;&#x7BA1;&#x9053;&#x7684;&#x8BFB;&#x5199;&#x4E24;&#x7AEF;&#x3002;&#x5176;&#x4E2D;fd[0]&#x8868;&#x793A;&#x8BFB;&#x7AEF;, fd[1]&#x8868;&#x793A;&#x5199;&#x7AEF;
&#x8FD4;&#x56DE;&#x503C;:&#x6210;&#x529F;&#x8FD4;&#x56DE;0&#xFF0C;&#x5931;&#x8D25;&#x8FD4;&#x56DE;&#x9519;&#x8BEF;&#x4EE3;&#x7801;
</unistd.h>

我们先用一个简单的例子来看一下管道的创建:

#include<iostream>
#include<unistd.h>
int main()
{
    int fd[2];
    int ret=pipe(fd);
    if(-1==ret)
    {
        std::cout<<"管道创建失败!"<<std::endl; } std::cout<<"fd[0]:"<<fd[0]<<std::endl<<"fd[1]:"<<fd[1]<<std::endl; return 0; < code></"管道创建失败!"<<std::endl;></unistd.h></iostream>

运行后:

Linux系统编程之匿名管道

可以看到,此时fd[0]和fd[1]返回了两个文件描述符。这两个文件描述符分别分别对应管道的读写两端。

#include<string.h>
#include<unistd.h>
#include<sys wait.h>
#include<sys stat.h>
#include<stdlib.h>
#include <sys types.h>
#include <fcntl.h>
int main()
{
  int fd[2];
  pipe(fd);
  pid_t pid = fork();
  if(pid < 0)
  {
    printf("fork error!");
  }else if(pid == 0)
  {
    //child
    close(fd[0]);
    char str[100];
    while(1)
    {
      printf("child:");
      fgets(str, 100, stdin);
      ssize_t len = strlen(str);
      if(write(fd[1], str, len) != len)
      {
        perror("write to pipe");
        exit(1);
      }
      memset(str, 0, len);
      sleep(1);
    }
  }
  //father
  int count = 0;
  close(fd[1]);
  while(count < 10)
  {
    char str[100];
    ssize_t s = read(fd[0], str, 100);
    if(s < 0){
      perror("read from pipe");
      break;
    }else{
      printf("father:%s", str);
    }
    memset(str, 0, strlen(str));
  }
  return 0;
}
</fcntl.h></sys></stdlib.h></sys></sys></unistd.h></string.h>

上面这段代码实现了子进程写入管道,父进程读出的过程。

Linux系统编程之匿名管道

2.3 管道的底层机制

管道是在有血缘关系的进程之间来通信的,如父子进程,兄弟进程等。因此,应用匿名管道时一定会有fork函数的参与。

如下面这个简化图可以看到,

  1. 父进程先使用pipe函数创建管道,得到两个文件描述符 fd[0]、fd[1]指向管道的读端和写端。
  2. 父进程调用fork创建子进程,此时父子进程有相同的struct files_struct,父子进程指向的struct file又指向了同一片文件缓冲区。(注意:这个表述并不严谨,我们下面马上就会讲到)
  3. 接下来父进程关闭写端,子进程关闭读端,就可以实现子进程向管道中写,父进程读。注意: 管道的通信是单向的!!!!

Linux系统编程之匿名管道

在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了 文件系统的file结构和VFS的 索引节点inode。通过 将两个 file struct指向同一个临时的 inode,而这个 VFS 索引节点又指向一个物理页面而实现的。

Linux系统编程之匿名管道

如上图所示, 有两个 file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。
这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了”Linux一切皆文件思想”。

2.4 管道读写规则

用阻塞的方式打开管道(即默认情况下)

  1. 如果 所有管道写端对应的文件描述符被关闭(管道写端引用计数为 0),读端在将管道中剩余数据读取后,再次 read会返回0。(写端关闭)
  2. 如果 有指向管道写端的文件描述符没关闭,且持有管道 写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次 read 会阻塞。(读完不写)
  3. 如果 所有指向管道读端的文件描述符都关闭了(管道读端引用计数为 0),进行write操作会产生信号SIGPIPE,进而可能导致write进程退出。(读端关闭)
  4. 如果 有指向管道读端的文件描述符没关闭(管道读端引用计数大于 0),且 读端进程并没有向管道中读进程,则当写端进程写满后,会进入阻塞。(写满不读)

2.5 管道的特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信。
  • 管道提供流式服务。
  • 管道的生命周期随进程,进程退出,管道释放。
  • 内核会对管道操作进行同步与互斥。
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
  • 管道大小为65536 byte

Original: https://www.cnblogs.com/Grong/p/15630119.html
Author: 乌有先生ii
Title: Linux系统编程之匿名管道

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585755/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 学习linux(centos7)记录的笔记

    此随笔用于记录学习《linux鸟哥的私房菜》过程中1.遇到的问题及解决的过程 2.有必要记录的重要内容 3.对应书上操作的记录 开始于2021年6月18号 一个磁盘的分区通过格式化…

    Linux 2023年6月6日
    090
  • 手把手搭建一个属于自己的在线 IDE

    背景 这几个月在公司内做一个跨前端项目之间共享组件/区块的工程,主要思路就是在 Bit 的基础上进行开发。Bit 主要目的是实现不同项目 共享 与 同步 组件/区块,大致思路如下:…

    Linux 2023年6月14日
    0114
  • 2021年3月-第01阶段-Linux基础-Linux系统概念-Linux命令

    Linux系统基本概念 图形界面: Ctrl+Shift +号 //调整命令终端变大 Ctrl – 号 //调整命令终端变小 命令终端: ~ 家目录:用户的私有场所,其…

    Linux 2023年6月8日
    0106
  • 类成员变量的初始化

    1-1 类成员变量初始化的分类 类成员变量的初始化可简单分为两类:非静态成员变量的初始化(以下简称”普通初始化”)和静态成员变量的初始化(”静态…

    Linux 2023年6月8日
    0119
  • mysql字符串拼接

    Mysql数据库中的字符串 CONCAT()CONCAT_WS()GROUP_CONCAT() CONCAT() CONCAT(string1,string2)最常用的字符串拼接方…

    Linux 2023年6月6日
    081
  • LeetCode-16. 最接近的三数之和

    题目来源 题目详情 给你一个长度为 n 的整数数组 nums和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。…

    Linux 2023年6月7日
    0108
  • Python函数的必选参数、默认参数、可变参数、关键字参数和命名关键字参数

    必选参数 def function(args_name): print (args_name) function("参数调用") ~$ 参数调用 跟在函数名口号…

    Linux 2023年6月7日
    076
  • Linux系统调用接口

    Linux系统调用接口 进程控制 系统调用 描述 fork 创建一个新进程 clone 按指定条件创建子进程 execve 运行可执行文件 exit 终止进程 _exit 立即终止…

    Linux 2023年6月13日
    0107
  • springBoot2.*使用redis集群/单机方法

    Lettuce 和 Jedis 的定位都是Redis的client,所以他们当然可以直接连接redis server。 Jedis在实现上是直接连接的redis server,如果…

    Linux 2023年5月28日
    0119
  • 分布式系统中数据存储方案实践

    数据膨胀的时候,必然放大细节。 一、背景简介 在项目研发的过程中,对于数据存储能力的依赖无处不在,项目初期,相比系统层面的组件选型与框架设计,由于数据体量不大,在存储管理方面通常容…

    Linux 2023年6月14日
    088
  • ElementUI table无缝循环滚动

    ElementUI table无缝循环滚动 恰好实习的时候遇到了这个需求,而且网上的代码有点僵硬,所以我改了改,顺手水一篇博客出来,其实是很简单的东西。 部分思路来源:https:…

    Linux 2023年6月7日
    0193
  • 数据库的灾备

    数据是企业重要的生产资料,关键数据的丢失可能会给企业致命一击,因为数据是计算机系统存在的原因和基础。数据往往是不可再生的,一旦发生数据丢失,企业就会陷入困境:客户资料、技术文件、财…

    Linux 2023年6月6日
    0113
  • 文件夹图标修改软件 FolderIco

    本来文件夹图标是可以自定义的,只要找好图片,在软件中把图片格式转换成ico,再在文件夹属性中设置图标就可以了。 但是我发现转换出来的ico不够清晰,只有256*256,在大图标模式…

    Linux 2023年6月6日
    0117
  • 域控制器所需的DNS SRV记录没有在DNS中注册的解决方法

    搭建完AD和DNS之后,发现在DNS的正向查找区域没有SRV记录,并且客户端无法加入到AD中,如下 解决方法 删除正向查找区域下的目录 然后选择”正向查找区域&#822…

    Linux 2023年6月14日
    0114
  • etcd 与 redis使用场景

    etcd etcd主要讲究服务发现, 有监听机制, 并能保持数据的一直性, 高可用 etcd的红火来源于kurbernetes用etcd做服务发现 etcd是一种分布式存储,更强调…

    Linux 2023年5月28日
    0103
  • [20220302]oracle如何定位使用library cache mutex 2.txt

    [20220302]oracle如何定位使用library cache mutex 2.txt –//这个问题实际上困扰我很久,我开始以为library cache b…

    Linux 2023年6月13日
    084
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球