【Linux】—— 匿名管道

news2024/11/26 5:49:34

前言:

  • 接下来我将带大家探索 进程间通信 的方式。本期,要讲的就是管道其中之一“匿名管道”!!

目录

(一)进程间通信介绍

1、进程间通信目的

2、进程间通信发展

3、进程间通信分类

(二)管道

1、什么是管道

2、站在文件描述符角度-深度理解管道

(三)管道分类

1、匿名管道 

2、场景分类

 3、管道读写规则

4、管道特点

(四)总结


(一)进程间通信介绍

进程间通信(Inter-process communication,IPC)是指操作系统中不同进程之间进行数据交换和通信的机制

1、进程间通信目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程;
  • 资源共享:多个进程之间共享同样的资源;
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程);
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
在一个计算机系统中,多个进程可能需要在运行时进行相互协作、共享数据或进行消息传递。为实现这些目的,操作系统提供了多种形式的进程间通信机制。

2、进程间通信发展

进程间通信(IPC)的发展一直与计算机领域的进步和需求密切相关。随着计算机技术的不断发展,IPC也经历了许多演进和改进,以满足不断增长的通信需求。以下是进程间通信发展的一些关键方面:

  • 管道
  • System V进程间通信
  • POSIX进程间通信

3、进程间通信分类

下面是几种常见的进程间通信机制:

管道
  1. 匿名管道pipe
  2. 命名管道
System V IPC
  1. System V 消息队列
  2. System V 共享内存
  3. System V 信号量
POSIX IPC
  1. 消息队列
  2. 共享内存
  3. 信号量
  4. 互斥量
  5. 条件变量
  6. 读写锁

这些进程间通信机制各有优缺点,选择合适的机制取决于应用程序的需求和特点。开发者需要考虑数据传输的速度、数据大小、并发性、可靠性等因素来选择适当的通信机制。


(二)管道

1、什么是管道

管道通信是消息传递的一种特殊方式(见下图):

  1. 所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件,又名pipe文件;
  2. 向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程)则从管道中接收(读)数据;
  3. 为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。

2、站在文件描述符角度-深度理解管道

【解释说明】

  • 上述描述了一个进程的默认文件符打开表,其中0、1、2是默认打开的,而3、4是分别使用读和写分别打开管道文件(当然反过来也是一样的)。

【解释说明】

  • 此时。父进程通过 fork 创建了子进程,只需把进程相关的数据结构调入,大家可以看成共享一片地址空间。

【解释说明】

  • 此时,我们关闭管道文件的读写端。就会形成一个单向通信的信道,至此双方就可以使用文件描述符对管道进行一个读一个写的操作,即实现了通信。


(三)管道分类

在Linux中,管道是一种用于进程间通信的特殊机制。根据使用方式和功能,Linux中的管道可以分为不同类型:

  1. 匿名管道(Anonymous Pipes): 匿名管道是最基本的管道类型,在命令行中使用竖线符号(|)来创建。它只能用于相关进程之间的通信,父进程与子进程之间或者同一管道链中的进程之间。

  2. 命名管道(Named Pipes): 命名管道(也称为FIFO)是一种有名字的管道,由mkfifo命令创建。它可以在磁盘上持久存在,并允许无关的进程之间进行通信。多个进程可以通过读取和写入相同的命名管道来进行数据交换。

1、匿名管道 

匿名管道是一种在进程间进行通信的机制,通常用于父子进程之间或者通过衍生的进程之间传递数据。匿名管道是一种单向通信机制,即数据只能从一个进程流向另一个进程

💨 接下来介绍匿名管道的一些关键特性:

1️⃣创建匿名管道(大家可以通过man手册进行查询)

  • 匿名管道通过系统调用(例如,在Unix/Linux中使用pipe函数)创建;
  • 创建管道时,操作系统会返回两个文件描述符,一个用于读取(读文件描述符),另一个用于写入(写文件描述符)

2️⃣下面是一个简单的C语言示例,演示了如何使用匿名管道在父子进程之间进行通信:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define BUFFER_SIZE 25

int main() {
    int pipefd[2]; // 用于存放管道两端文件描述符的数组
    pid_t pid;
    char message[BUFFER_SIZE] = "Hello, child process!";

    // 创建管道
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // 创建子进程
    pid = fork();

    if (pid < 0) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid > 0) { // 父进程
        close(pipefd[0]); // 关闭用于读的文件描述符

        // 向管道写入消息
        write(pipefd[1], message, strlen(message) + 1);

        // 关闭用于写的文件描述符
        close(pipefd[1]);

        printf("Parent process: Message sent to child.\n");
    } else { // 子进程
        close(pipefd[1]); // 关闭用于写的文件描述符

        // 从管道读取消息
        char received_message[BUFFER_SIZE];
        read(pipefd[0], received_message, sizeof(received_message));

        // 关闭用于读的文件描述符
        close(pipefd[0]);

        printf("Child process: Received message - %s\n", received_message);
    }

    return 0;
}

2、场景分类

💨当读的一方 read 完所有的管道数据,如果写的一方不往管道里面发送数据,此时就只能等待:

 int main()
 {
      // 任何一种任何一种进程间通信中,一定要 先 保证不同的进程之间看到同一份资源
      int pipefd[2] = {0};
      //1. 创建管道
      int n = pipe(pipefd);
      if(n < 0)
      {
          std::cout << "pipe error, " << errno << ": " << strerror(errno) << std::endl;
          return 1;
      }
      std::cout << "pipefd[0]: " << pipefd[0] << std::endl; // 
      std::cout << "pipefd[1]: " << pipefd[1] << std::endl; // 
      
      //2. 创建子进程
      pid_t id = fork();
      assert(id != -1); //正常应该用判断,我这里就断言:意料之外用if,意料之中用assert
  
      if(id == 0)    // 子进程
      {
          //3. 关闭不需要的fd,让父进程进行读取,让子进程进行写入                                                  
          close(pipefd[0]);
  
            //4. 开始通信 -- 结合某种场景
           const std::string namestr = "hello, 我是子进程";
           int cnt = 1;
           char buffer[1024];
  
           while(true)
           {
              snprintf(buffer, sizeof buffer, "%s, 计数器: %d, 我的PID: 
                       %d",namestr.c_str(), cnt++, getpid());
              write(pipefd[1], buffer, strlen(buffer));
              sleep(10);
           }
                                                                                                                   
            close(pipefd[1]);
            exit(0);
  
        }
  
       //父进程
      //3. 关闭不需要的fd,让父进程进行读取,让子进程进行写入
      close(pipefd[1]);
  
      //4. 开始通信 -- 结合某种场景
      char buffer[1024];
      while(true)
      {
           int n = read(pipefd[0], buffer, sizeof(buffer) - 1);
          if(n > 0)
          {
              buffer[n] = '\0';
              std::cout << "我是父进程, child give me message: " << buffer << std::endl;
          }
      }
  
      close(pipefd[0]);
      return 0;
  }


💨当写的一方不往管道里面 write 数据,而读的一方此时不读,当管道写满之后就不能在继续写数据(即管道有大小限制):

输出结果:

 


💨当此时关闭了写数据的一方,读取完毕管道数据。如果在继续读取的话,就会 read 返回0,表明读到文件结尾。

输出结果:

 


💨当写端一直写,读端关闭,此时就没有意义,OS不会维护无意义、低效率的事件,会通过 13号 信号来杀死一直写入的进程:

输出结果:


 3、管道读写规则

🔥 当没有数据可读时:
  1. O_NONBLOCK disableread调用阻塞,即进程暂停执行,一直等到有数据来到为止。
  2. O_NONBLOCK enableread调用返回-1errno值为EAGAIN

🔥 当管道满的时候:
  • O_NONBLOCK disable write调用阻塞,直到有进程读走数据
  • O_NONBLOCK enable:调用返回-1errno值为EAGAIN
🔥 如果所有管道写端对应的文件描述符被关闭,则read 返回 0;
🔥 如果所有管道读端对应的文件描述符被关闭,则write 操作会产生信号 SIGPIPE , 进而可能导致 write 进程退出;
🔥 当要写入的数据量不大于 PIPE_BUF 时, linux 将保证写入的原子性;
🔥 当要写入的数据量大于PIPE_BUF 时, linux 将不再保证写入的原子性。

4、管道特点

通过上述,简单总结一下管道有哪些特点:

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  2. 管道提供流式服务 ;
  3. 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  4. 一般而言,内核会对管道操作进行同步与互斥
  5. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道


(四)总结

接下来,简单总结一下本期关于“匿名管道”的全部知识!!!

  1. 使用匿名管道可以方便地在父子进程之间传递数据,但也有一些限制,如只能用于具有亲缘关系的进程间通信,无法用于无关进程之间的通信;
  2. 此外,管道的容量有限,如果写入速度超过读取速度,可能会导致阻塞;
  3. 在实际使用中,可以根据需求选择其他更复杂的进程间通信机制,如命名管道、共享内存或消息队列等。

以上便是本期关于匿名管道的全部内容了,感谢大家的观看与支持!!!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1349827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024年01月编程语言流行度排名

点击查看最新编程语言流行度排名&#xff08;每月更新&#xff09; 2024年01月编程语言流行度排名 编程语言流行度排名是通过分析在谷歌上搜索语言教程的频率而创建的 一门语言教程被搜索的次数越多&#xff0c;大家就会认为该语言越受欢迎。这是一个领先指标。原始数据来自…

linux休眠机制介绍

一、概述 Linux系统提供了休眠和低功耗模式&#xff0c;可以帮助节省电力和延长电池寿命&#xff0c;休眠对应的另外一种模式就是唤醒。 二、常用的休眠方式 常用的休眠方式有freeze,standby, mem, disk&#xff0c;hibernate freeze: 冻结所有的进程&#xff0c;包括用户空…

微信小程序-页面开发

文章目录 微信小程序第二章2. 页面开发2.1 创建开发页面2.2 修改项目首页2.3 页面的结构和样式设计2.3.1 WXML结构设计2.3.1.1 什么是WXML2.3.1.2 WXML的常见标签2.3.1.3 WXML的特点 2.3.2 WXSS样式设计2.3.2.1 什么是WXSS 2.4 组件库的使用和自定义组件2.4.1 小程序中的组件分…

基于 Haar 特征的人脸检测

Haar分类器概述 Haar分类器采用的是Viola-Jones人脸检测算法 该算法需要用到大量的积极图片&#xff08;包含人脸的图片&#xff09;和消极图片&#xff08;不包含人脸的图片&#xff09;&#xff0c;从中提取类Haar特征( Haar-like features)&#xff0c;经过反复训练&#…

TF-IDF(Term Frequency-Inverse Document Frequency)算法 简介

TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是一种用于信息检索和文本挖掘的常用算法。它用于评估一个词对于一个文档集合中某个文档的重要性。 这个算法的基本思想是&#xff1a;如果一个词在一个文档中频繁出现&#xff0c;并且在整个文档集合…

MySQL基础笔记(1)基础理论

一.基本概念 DB&#xff1a;数据库&#xff0c;存储数据的仓库&#xff0c;数据是有组织地进行存储DBMS&#xff1a;操纵和管理数据库的大型软件 SQL&#xff1a;结构化查询语言&#xff0c;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库的统一标准 &…

CSDN规则详解(二)

文章目录 每日一句正能量前言博文发布说明创建专栏显示数量上限问题私信规则说明会员下载次数说明后记 每日一句正能量 与其战胜敌人一万次&#xff0c;不如战胜自己一次。 前言 在数字技术的浪潮下&#xff0c;中国的开发者社区蓬勃发展&#xff0c;而CSDN则是其中的佼佼者。…

[蓝桥杯知识学习] 树链

DFS序 什么是DFS序 怎么求DFS序 进入操作&#xff0c;将有计数 出&#xff1a;可以理解为&#xff0c;没有孩子可以去了&#xff08;不能&#xff0c;向下行动&#xff1a;对应于程序里的入栈&#xff09;&#xff0c;所以回到父结点&#xff08;向上行动&#xff0c;对应于程…

我在CSDN的2023年

一、引言 在2023年的这一年当中&#xff0c;在CSDN的生活让我得到许多知识与启发&#xff0c;也让我获得一些快乐和成就 二、自己的收获 在这一年当中&#xff0c;我从一个只会看别人写的文章解决问题到&#xff0c;可以自己写文章帮别人解决问题&#xff0c;这种成就感是极大…

JavaScript history对象常用方法【通俗易懂】

✨前言✨   本篇文章主要在于了解及使用JavaScript中history对象常用方法 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&#xff0c;友友们有任何问题可以在评论区留言 文章目录 一&…

【熔断限流组件resilience4j和hystrix】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容起因resilience4j落地实现pom.xml依赖application.yml配置接口使用 hystrix 落地实现pom.xml依赖启动类上添加注解接口上使用 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟…

thinkphp6入门(14)-- 多关联模型查询

背景&#xff1a; 有3个数据表&#xff0c;一个User表&#xff0c;一个Cloth表&#xff0c;一个Shoe表。 Cloth表和Shoe表分别和User表通过user_id关联。 thinkphp 6中如何通过模型查询所有用户&#xff0c;其中包括每个用户的cloth和shoe。 多关联模型查询&#xff1a; 1.…

热塑性塑料激光透光率检测仪

热塑性塑料是一类在一定温度下具有可塑性&#xff0c;冷却后固化且能重复这种过程的塑料。它们通常由线型高分子化合物组成&#xff0c;这些分子在受热时不会发生交联。热塑性塑料可以通过回收和再加工&#xff0c;重新制成新的产品。根据其性能特点、用途广泛性和成型技术通用…

YOLOv8改进 | 2023主干篇 | FasterNeT跑起来的主干网络( 提高FPS和检测效率)

一、本文介绍 本文给大家带来的改进机制是FasterNet网络&#xff0c;将其用来替换我们的特征提取网络&#xff0c;其旨在提高计算速度而不牺牲准确性&#xff0c;特别是在视觉任务中。它通过一种称为部分卷积&#xff08;PConv&#xff09;的新技术来减少冗余计算和内存访问。…

漏洞复现-海康威视网络对讲广播系统远程命令执行漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

策略模式示例,Lambda表达式

这样会有很多代码冗余 以上代码使用策略模式优化 优化方案1 那么现在可以这样 优化方案二 原先定义了接口,还需要一个个写实现类,其实完全没必要,用匿名内部类方式就可以 优化方案三:用lambda方式简写 优化方案四:不需要定义接口 使用Stream API

JavaScript中alert、prompt 和 confirm区别及使用【通俗易懂】

✨前言✨   本篇文章主要在于&#xff0c;让我们看几个与用户交互的函数&#xff1a;alert&#xff0c;prompt 和confirm的使用及区别 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&…

【Shell编程练习】猜大小

系列文章目录 输出Hello World 通过位置变量创建 Linux 系统账户及密码 监控内存和磁盘容量&#xff0c;小于给定值时报警 系列文章目录脚本生成一个 100 以内的随机数,提示用户猜数字&#xff0c;根据用户的输入&#xff0c;提示用户猜对了&#xff0c;猜小了或猜大了&#x…

CMake入门教程【基础篇】CMake是什么?为什么学习CMake

文章目录 1.CMake简介2.为什么要学习CMake3.什么样的项目需要用CMake3.1大型或复杂项目3.2跨平台项目3.3需要高度定制化构建的项目3.4 研究和开源项目3.5小型或简单项目 4.CMake的作用5.支持的编译器5.1Windows平台5.2Unix/Linux平台5.3macOS平台5.4其他编译器5.5支持的平台 CM…

undefined reference to `pthread_create‘的另外一种解法

背景 编译带有thread的程序人&#xff0c;如果忘记-lpthread&#xff0c;那么就会报错 解决办法一&#xff1a;添加-lpthread 很简单添加-lpthread就行了 解决办法二&#xff1a;升级glibc 在高版本的glibc上&#xff0c;可能无需增加-lpthread Why glibc 2.34 removed li…