【Linux】进程间通信 -- 匿名管道 | pipe系统调用

news2024/11/17 9:47:04

  • 什么是通信?
  • 为什么要有通信?
  • 如何实现?
    • 管道通信
      • 匿名管道
    • pipe系统调用
      • 读写特征
      • 管道的特征:

什么是通信?

进程具有独立性,我们现在的进程间需要通信,那么这个成本一定不低

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

为什么要有通信?

有时候我们需要多进程协同操作,比如:cat file.txt | grep hello'

在这里插入图片描述
两个进程通过管道的方式协同操作

如何理解通信的本质问题?

  1. 操作系统OS需要直接或间接的给通信双方的进程提供“内存空间”,这个“内存空间”肯定不是属于通信双方的,因为进程具有独立性,如果要存在,要么是一方的要么就是另一方的,所以这个“内存空间”,肯定是由“第三方”也就是OS提供的
  2. 要通信的进程,必须看到一份公共的资源

不同的通信种类:
本质就是:上面所说的资源,是由OS中的哪一个模块提供的。

不同的进程实现通信,第一条件就是需要先让不同的进程看到同一份资源(我们主要学习的就是这个)

如何实现?

进程间通信分类,标准方法:

  • POSIX – 通信过程可以跨主机
  • System V – 聚焦在本地通信

管道通信

跟上面两种不一样,管道是基于文件系统的通信方式:

  • 匿名管道
  • 命名管道

什么是管道?

  • 管道是Unix中最古老的进程间通信的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道"

匿名管道

由父进程fork创建子进程来使得两个进程看到同一个管道文件,这种管道叫做匿名管道

在这里插入图片描述
匿名管道它是在内存中创建的一段缓冲区,可以用于在两个进程之间传递数据。由于匿名管道是在内存中创建的,因此不需要与磁盘进行IO。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

使用匿名管道进行进程间通信时,数据是从一个进程中写入管道,然后从另一个进程中读取。这个过程是通过操作系统内核来实现的,因此不需要涉及磁盘IO操作。操作系统会负责将数据从一个进程的地址空间复制到管道缓冲区中,并将数据从管道缓冲区复制到另一个进程的地址空间中。

匿名管道是一种半双工的通信机制,即只能在一个方向上传递数据。如果需要进行双向通信,需要创建两个管道。匿名管道目前能用于父子进程之间的通信

父进程需要分别以读和写打开同一个文件,是因为管道是一个特殊的文件,它只存在于内存中,没有对应的磁盘文件。管道是单向的,所以需要使用两个文件描述符来实现双向通信。父进程需要以读模式打开管道的读取端,以便能够从管道读取数据;同时,父进程也需要以写模式打开管道的写入端,以便能够向管道写入数据。因此,父进程需要分别以读和写模式打开同一个文件,来实现对管道的读写操作。

pipe系统调用

pipe的用法:
在这里插入图片描述

#include <iostream>
#include <cassert>
#include <unistd.h>
using namespace std;
int main()
{
    int fds[2];
    int n = pipe(fds);
    assert(n==0);
    //[0]是读取
    //[1]是写入
    cout<<"fds[0]:"<<fds[0]<<endl;
    cout<<"fds[1]:"<<fds[1]<<endl;
    return 0;
}

在这里插入图片描述
在Linux中,每个进程都有一个内核空间和一个用户空间。管道在内核空间中实现,而进程在用户空间中实现。当一个进程调用pipe()函数时,内核会创建一个管道,并返回两个文件描述符,一个是读取端的文件描述符,一个是写入端的文件描述符。这两个文件描述符都是指向内核中的管道,而不是指向文件系统中的文件。

当一个进程想要向管道中写入数据时,它将数据写入到写入端的文件描述符中。这个数据将被存储在管道中,等待其他进程读取。当一个进程想要从管道中读取数据时,它从读取端的文件描述符中读取数据。这个读取操作会从管道中删除一个数据,其他数据会向前移动。

在Linux中,管道的实现是通过环形缓冲区来实现的。当数据被写入管道时,它会被存储在环形缓冲区中。当数据被读取时,它会从环形缓冲区中删除。如果缓冲区已满,写入操作将会阻塞,直到有一些数据被读取出来。如果缓冲区为空,读取操作将会阻塞,直到有一些数据被写入进来。

管道通信代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

using namespace std;

// 父进程进行读取,子进程进行写入
int main()
{
    // 第一步:创建管道文件,打开读写端
    int fds[2];
    int n = pipe(fds);
    assert(n == 0);

    // 第二步: fork
    pid_t id = fork();
    assert(id >= 0);
    if (id == 0)
    {
        // 子进程进行写入,那么它就要关闭读取端
        close(fds[0]);
        // 子进程的通信代码
        const char *s = "我是子进程,我正在给你发消息";
        int cnt = 0;
        while (true)
        {
            cnt++;
            char buffer[1024]; // 只有子进程能看到!
            snprintf(buffer, sizeof buffer, "child->parent say: %s[%d][%d]", s, cnt, getpid());
            write(fds[1], buffer, strlen(buffer));
            cout << "count: " << cnt << endl;
            sleep(1); //细节,我每隔1s写一次
        }
        exit(0);
    }
    // 父进程进行读取,那么它就要关闭写入端
    close(fds[1]);
    // 父进程的通信代码
    while (true)
    {
        char buffer[1024];
        ssize_t s = read(fds[0], buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            cout << "Get Message# " << buffer << " | my pid: " << getpid() << endl;
        }
        // 细节:父进程可没有进行sleep
    }
    n = waitpid(id, nullptr, 0);
    assert(n == id);
    close(fds[0]);
    return 0;
}

输出结果:
在这里插入图片描述
上面的代码子进程全程没有往显示器打印消息,打印的消息全是父进程在做,这种通信方式也就叫管道通信

为什么我们在使用的时候是需要用close(fds[0]);这种方式去关闭,而不是直接以数字为关闭符号,因为虽然我们知道012是以及被标准输入输出错误提前占领,我们接下来就是使用的34号位,但是我们不能够确定前面没有创建过文件,而将位置已经占了,所以我们最好用fds[0]这种方式

在这里插入图片描述

如果管道中没有了数据,读端在读,默认会直接阻塞当前正在读取的进程!

管道是一段固定大小的缓冲区,如果写端满了的时候,再写会阻塞,等对方进行读取!而此时要读取是直接将整个缓冲区有的全读取,不是一行一行读取

我们在读取sizeof(buffer) - 1/strlen(buffer)计算长度大小的时候有时候减1有时候不减有时候加1有时候不加,是因为C语言到文件系统,文件系统不需要以\0结尾,所以我们不需要多加1传入\0,而文件到C语言,我们需要多留出一个位置来存放\0

读写特征

  1. 读慢,写快
  2. 读快,写慢
  3. 写关闭,读到0(终止符号)截至
  4. 读关闭,OS会给进程发送信号,终止写端

验证4:我们使子进程一直写,然后父进程只写一次后关闭自己的读端,我们观察一下进程的退出:

	//上面代码不用更改,只需要子进程改为不睡眠一直写
	close(fds[0]);
    cout << "父进程关闭读端" << endl;
    int status = 0;
    n = waitpid(id, &status, 0);
    assert(n == id);
    cout <<"pid->"<< n << " : "<< (status & 0x7F) << endl;

在这里插入图片描述

[AMY@VM-12-15-centos lesson_14]$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

我们可以发现,当父进程关闭读端,子进程会被终止,且其终止信号为:SIGPIPE

管道的特征:

  1. 管道的生命周期是随进程。 进程退出,管道在内核中所对应的资源就释放
  2. 管道可以用来进行具有血缘关系的进程之间进行通信,常用与父子通信
  3. 管道是面向字节流的(网络)
  4. 半双工–单向通信(特殊概念)
  5. 互斥与同步机制–对共享资源进行保护的方案

如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

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

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

相关文章

@FeignClient源码浅析

Spring如何识别FeignClient 从EnableFeignClients 出发&#xff0c;寻找Spring如何识别FeignClient 从源码中查看到Import(FeignClientsRegistrar.class) Retention(RetentionPolicy.RUNTIME) Target(ElementType.TYPE) Documented Import(FeignClientsRegistrar.class) pub…

PyQt结合OpenCV实现实时人流量统计

1. 废话篇&#xff08;可跳过&#xff09; 之前学的基本都是Web端的技术。前两天的面试&#xff0c;让我深入的去学习一下 Qt 技术&#xff0c;了解完概念之后&#xff0c;才知道我之前接触的类 TkInter 技术&#xff0c;有点安卓开发的味道。。。 2. 人流量统计效果图 3. 业务…

L1-027 出租(Python实现) 测试点全过

题目 下面是新浪微博上曾经很火的一张图&#xff1a; 一时间网上一片求救声&#xff0c;急问这个怎么破。其实这段代码很简单&#xff0c;index数组就是arr数组的下标&#xff0c;index[0]2 对应 arr[2]1&#xff0c;index[1]0 对应 arr[0]8&#xff0c;index[2]3 对应 arr[…

Php Jenkins phpunit配置

目录 作用 前提 安装 安装xUnit插件 win10重启Jenkins 全局环境设置 创建项目配置 描述 源码管理 构建触发器 构建步骤 插件安装 工作空间 php代码phpunit文件示例 项目根目录配置 phpunit.xml Protect/Tests/test_start.php composer.json 作用 jenkins 自动…

【docker部署安装ApiSix】

docker安装ApiSi 常见问题-提前查阅 1-端口被占用 确保所需的所有端口&#xff08;默认的 9080/9091/9443/2379/9000&#xff09;未被其他系统/进程使用 #查询端口占用情况 netstat -antp |grep 9443# 如果端口冲突可尝试修改apisix的端口配置&#xff0c; # 但不建议&#x…

0305kali linux配置运行-docker-macos aarm64

文章目录 1 下载运行2 配置2.1 配置系统环境2.2 配置SSH服务2.3 安装工具 3 问题总结结语 1 下载运行 拉取kali linux镜像 docker pull kalilinux/kali-rolling该镜像为“纯净版”系统&#xff0c;没有任何工具&#xff0c;体积小。下面当我们运行起来之后&#xff0c;到容器中…

GlusterFs 分布式复制卷(Distributed-Replicate)性能测试

目录 fio工具参数解释 Glusterfs 和NFS 性能测试 顺序写&#xff1a; 随机写&#xff1a; 顺序读&#xff1a; 随机读&#xff1a; 随机读写&#xff1a; 参数说明&#xff1a; 测试结论&#xff1a; 与NFS对比 压测对比结果 NFS和GlusterFs的优缺点 NFS的优点 NFS…

基于卷积神经网络VGG的猫狗识别

&#xff01;有需要本项目的实验源码的可以私信博主&#xff01; 摘要&#xff1a;随着大数据时代的到来&#xff0c;深度学习、数据挖掘、图像处理等已经成为了一个热门研究方向。深度学习是一个复杂的机器学习算法&#xff0c;在语音和图像识别方面取得的效果&#xff0c;远远…

综合能源系统(1)——综合能源系统基本定义与内涵

综合能源系统关键技术与典型案例  何泽家&#xff0c;李德智主编 综合能源系统基本定义 综合能源系统(Integrated Energy System&#xff0c;IES)的概念最早产生于热电联产领域&#xff0c;侧重于热电系统的协同优化&#xff0c;而后逐渐扩展丰富&#xff0c;涉及电、热、冷…

DEVICENET转MODBUS-TCP网关应用案例

远创智控YC-DNT-TCP连接到DEVICENET总线中做为从站使用&#xff0c;连接到 MODBUS-TCP 总线中做为主站或从站使用。是自主研发的一款 DEVICENET 从站功能的通讯网关。 YC-DNT-TCP常用拓展图 技术指标 网关的 MODBUS 接口可通过拨码选择做为主站&#xff08;客户端&#xff09…

QNAP威联通NAS搭建SFTP服务,并内网穿透实现公网远程访问

文章目录 前言1. 威联通NAS启用SFTP2. 测试局域网访问3. 内网穿透3.1 威联通安装cpolar内网穿透3.2 创建隧道3.3 测试公网远程访问 4. 配置固定公网TCP端口地址4.1 保留一个固定TCP端口地址4.2 配置固定TCP端口地址4.3 测试使用固定TCP端口地址远程连接威联通SFTP 转载自远程内…

【算法集训之线性表篇】Day 05

文章目录 题目思路代码实现效果 题目 将两个有序顺序表合并为一个有序顺序表&#xff0c;函数结果返回值为顺序表。 思路 我们可以利用二路归并排序算法中的Merge函数思路&#xff0c;设置两个指针i&#xff0c;j&#xff0c;分别记录在顺序表a和b中的访问位置&#xff0c;再…

【未解决】No rule to make target ‘/usr/lib/x86_64-linux-gnu/libGL.so‘

测试ros自带的PCL1.8是否能用&#xff0c;网上找个测试代码&#xff0c;编译阶段报错&#xff1a; cmake .. -- Could NOT find ensenso (missing: ENSENSO_LIBRARY ENSENSO_INCLUDE_DIR) ** WARNING ** io features related to ensenso will be disabled -- Could NOT find …

Blender环境纹理材质贴图入门教程

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D开发工具链 大家好&#xff0c;今天跟大家分享Blender材质贴图入门图文教程&#xff0c;一套blender的PBR材质包&#xff0c;和HDRI环境纹理贴图&#xff0c;在文末领取&#xff0c;希望能助到大家更高效完成场景练习。 据我了解…

Linux Deploy(一)Linux Deploy简介与软件安装

一、Linux Deploy简介 Linux Deploy可以在安卓机器上使用chroot容器技术运行arm或者x86的Linux系统&#xff0c;利用该技术可以搭建个人服务器&#xff0c;Linux Deploy可运行多种linux发行版的软件&#xff0c;不失为一个好的家庭微型服务系统&#xff0c;如果想把手机弄成微…

【Spark_BigData】期末复习考试——

复习题目 yarn框架中不包含的进程为 Yarn包括两个主要进程:资源管理器Resource-Manager,节点管理器Node-Manager。 Scheduler zookeeper spark SQL 前身 Shark 在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格。 HiveContext继承自SQLCon…

安装最新版CMAK,处理报错java.util.NoSuchElementException: key not found: PLAINTEXT

安装最新版CMAK&#xff0c;处理报错java.util.NoSuchElementException: key not found: PLAINTEXT 一、下载CMAK二、解压CMAK三、修改配置文件四、安装jdk11五、启动CMAK六、CMAK界面设置Kafka集群信息七、完整报错八、报错原因九、解决方法 一、下载CMAK CMAK下载地址&#…

将本地项目上传至gitee

一、先去gitee新建仓库 点击新建仓库之后&#xff0c;跳转下图页面 点击创建之后&#xff0c;跳转下图页面 到此为止&#xff0c;这里仓库就创建好了&#xff0c;下面去提交代码 二、本地项目连接远程仓库 1、进入想上传的项目的文件夹&#xff0c;然后右键点击 Git Bash Her…

Stable Diffusion 模特换装 蒙版一键批量提取

有没有想过可以使用算法批量提取图片中模特的服装&#xff0c;然后通过SD进行换装。 一个一个的PS抠图是不是太累&#xff0c;可以使用算法批量提取。相对于 Segment Anything 方法这个比较简单。 文章目录 蒙版批量提取SD换装 蒙版批量提取 import osfrom tqdm import tqdm …

前台测试,工程督导及5G网络优化工程师的区别具体在哪里?

什么是通信? 北邮版《通信原理》教材的第一句话是--通信乃是互通信息。 其实&#xff0c;这句话就说出了通信的本质&#xff0c;通信的目标就是如何让世界的任何人在任何时间&#xff0c;任何地点都进行信息的互通。 大到卫星&#xff0c;小到SIM卡&#xff0c;通信技术覆盖于…