Linux学习之路 -- 进程 -- 进程间通信 -- 管道通信

news2024/9/22 19:29:20

本文主要介绍进程通信中的管道通信。

前面我们学习进程的过程中,我们知道,进程是具有独立性的。这也就导致了进程不能够直接地把数据进行传递。为了实现进程之间地通信,我们就需要通过另外地方式来实现进程之间数据地传递。

1.进程通信的目的

首先,在正式学习进程间通信前,我们需要了解进程间通信的目的

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

总的来说,就是我们往往需要多个进程进行协作,完成一些事情。

2.管道通信的相关原理

1.一般规律

        假设我们有两个进程,我们要实现这两个进程之间的数据通信,肯定是不能把一个进程上的数据直接拷贝到另一个进程的空间上,这样的作法无法保证进程之间的独立性。所以我们需要通过一块(内存)空间来实现两个进程之间的数据。同时,这块空间还不能由通信双方任何一个提供,如果是由其中一个提供,那就允许另一个进程访问,这会破坏进程的独立性。

        所以进程间通信的本质就是让不同的进程看到同一块空间资源,这块资源一般由OS提供。而OS提供的“空间”有不同的样式,就决定了不同的通信方式。
2.实现方式

        前面我们在介绍文件时,我们了解到进程是通过文件描述符表来控制文件的。其中一个文件被不同的方式打开是要占据不同的文件描述符的,而我们再创建一个子进程时,文件描述符表也会跟着创建一份,但是里面的内容是和父进程一致的。这两个进程都会指向同一个文件。


这里父子进程就指向了同一块空间,并且这块空间是由操作系统提供,说明我们可以通过文件的方式,来实现进程之间的通信。这种通信的方式就叫做管道通信

管道通信只能被设计成单向的通信,也就是一个进程读,另一个进程写。正常情况下,我们要以读方式和写方式分别打开两次文件,在不同的进程中关闭不同的文件描述符,这样做是为了让父子进程都可以当作读端或写端。我们把父进程以读方式打开的文件描述符关闭,把子进程以写方式打开的文件描述符关闭,这样就可以实现父进程写,子进程读。

相关接口

为了支持管道通信,系统给我们提供相关的系统接口

<1>pipe

int pipefd[2]是输出型参数,用于存放两个fd,分别是以读和写方式打开的文件描述符。通过该接口,我们就不需要向磁盘中刷新和向磁盘中创建文件。通俗的说,就是创建内存级的文件,叫匿名文件(管道)。这个文件不用把数据加载到磁盘,也不用实现标准输入、输出、错误等等。

匿名管道通信的特点,就是只能让有血缘关系的进程,进行进程间通信(常用于父子进程)

这个接口如果返回零,那么就表示调用成功,如果失败了,就返回-1。如果成功调用,那么pipefd[0]中存放的是读端的文件描述符,而pipefd[1] 中存放的是写端的文件描述符。

下面我们可以用一段代码验证上述的结论

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

int main()
{
    int pipefd[2];
    int n = pipe(pipefd);
    if(n < 0) return 1;
    printf("%d %d\n",pipefd[0],pipefd[1]);
    return 0;
}

运行结果

下面简单实现一下,用父子进程间进行通信

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>

void Write(int wfd)
{
    const char* str = "hello linux";
    char buffer[1024];
    int pid = getpid();
    while(1)
    {
        snprintf(buffer,sizeof(buffer),"pid:%d str:%s\n",pid,str);
        write(wfd,buffer,sizeof(buffer));
    }

}
void Read(int rfd)
{
    int cnt = 20;
    char buffer[1024];

    while(cnt--)
    {
        ssize_t n = read(rfd, buffer,sizeof(buffer));
        (void)n;
        printf("%s", buffer);
        sleep(1);
    }
}

int main()
{
    int pipefd[2];
    int n = pipe(pipefd);
    if(n < 0) return 1;
    printf("%d %d\n",pipefd[0],pipefd[1]);
    pid_t id = fork();
    //写端
    if(id == 0)
    {
        close(pipefd[0]);
        Write(pipefd[1]);
        exit(0);
    }
    //父进程
    close(pipefd[1]);
    Read(pipefd[0]);
    wait(NULL);
}

 运行结果

 这样我们就实现父子进程的简单通信。

关于管道通信的几种情况。

<1>管道内部没有数据 && 子进程不关闭自己的写端文件fd,读端(父进程)就要阻塞等待,直到管道有数据。

<2>管道内部被写满了 && 读端(父进程)不关闭自己的fd,写段(子进程)写满之后,就要阻塞等待。管道的默认大小是4kB(unbantu 20.04版本下)。在这种情况下,读端会尽可能多的读取数据,当读取到一定数量的数据时,写端又会重新向管道写入数据。

<3>对于写端而言,不写了&&关闭了管道,读端会将pipe中的数据读完,最后就会读到返回值为0,表示读结束,类似与读到文件的结尾。

<4>读端不读&&关闭,写端在写,OS会直接终止写入的进程(子进程),通过信号(13 SIGPIPE)进程终止。(下图是让读端关闭(父进程),写端(子进程)继续写,并打印出退出码和退出信号)。

管道的几种的特性

<1>自带同步机制,也就是执行时有一定的顺序。

<2>有血缘关系之间的通信

<3>管道是面向字节流的(读端和写端的次数没有直接的联系)

<4>父子进程退出,管道自动的释放,文件的声明周期是随进程的。

<5>管道只能单向通信。半双工的一种特殊情况

而我们学习的命令行管道,本质上也就是本文所述的管道。而我们在使用命令行管道时,一个命令就是一个进程,一个竖划线就是一个管道,这些进程的父进程都是bash进程。

应用场景:进程池。

由于我们每次创建进程都要向系统中进行申请,这个过程比较麻烦,所以我们可以直接先申请多个进程,由一个主进程进行控制,每个进程都和父进程之间创建管道,这个就叫进程池。在创建完毕后,我们可以进程池内的进程分配任务,一个进程不能执行全部的任务,而是要让所有的进程都执行一些任务,这个分配规则就叫负载均衡。

命名管道

匿名管道适用于父子进程之间的通信,而我们如果要在完全不相干的两个进程之间进行通信,就需要使用命名管道。

如上图所示,当我们进程A和进程B以不同的方式打开file.txt时,正常来说会生成两个文件缓冲区,但是由于两个文件缓冲区内容是一样的,所以我们就只需要一个文件缓冲区即可。由于此时的文件缓冲区是不需要向文件中刷新数据的(会浪费空间,而且没必要),所以文件缓冲区就可以作为一个管道,实现两个进程之间的通信。

如何保障两个打开的是同一个文件呢(也就是确保同一缓冲区)?我们可以使用文件的路径+文件名的方式锁定文件,这样可以保证打开的就是同一文件。

具体方法:

我们可以使用mkfifo命令创建管道,然后在实现两个进程之间的通信。

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

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

相关文章

训练 Transfomer 模型的内存消耗计算

目录 model 内存gradients 内存activates 内存 经典图打底&#xff1a; 训练深度模型的内存消耗主要有以下几个部分&#xff1a; 存储模型可训练参数存储梯度存储反向传播中间变量&#xff0c;例如&#xff1a; L ( Y − Y ^ ) 2 Y ^ X T W ∂ L ∂ W − 2 ( Y − Y ^ ) …

Transformer架构;Encoder-Decoder;Padding Mask;Sequence Mask;

目录 Transformer架构 Transformer架构的主要组成部分: 简单举例说明输入和输出: Encoder-Decoder 编码器/解码器组成 6、位置前馈网络(Position-wise Feed-Forward Networks) 7、残差连接和层归一化 10、掩码Mask 10.1 Padding Mask 10.2 Sequence Mask 为什么…

Gradio 复杂布局的实现

Gradio Interface 和 ChatInterface 布局都相对固定&#xff0c;只能通过参数添加组件&#xff0c;如果想要自定义页面布局&#xff0c;就需要更高级的布局方式 Block 。Gradio 中可以通过行和列进行布局&#xff0c;可以互相嵌套。我们先看一官方的例子&#xff1a; import g…

Vue Mixins 深度解析含面试常问题

Vue Mixins 深度解析含面试常问题 文章目录 Vue Mixins 深度解析含面试常问题一、Mixin 是什么二、Vue中如何使用1. 创建Mixin2. 使用Mixin3. 合并策略4. 全局Mixin5. 使用场景 三、包含哪些属性或方法API四、扩展与高级技巧1. 命名冲突2. 全局 vs 局部3. 合并策略深入4. 使用高…

商品期权会爆仓吗?

商品期权交易中存在爆仓的情况。一个期权的价格与其基础资产的波动性密切相关。在波动性高的情况下&#xff0c;尽管收益可能更高&#xff0c;但投资者也需要面对更大的价格波动风险&#xff0c;商品期权有买方和卖方&#xff0c;买方无爆仓风险&#xff0c;卖方是保证金交易有…

Hadoop大数据集群搭建

一、虚拟机配置网络 1、配置文件 进入“/etc/sysconfig/network-scripts”目录&#xff0c;查看当前目录下的“ifcfg-ens33”文件 对“ens33”文件进行配置 2、重启网络 systemctl restart network 3、测试网络 Ping www.baidu.com 4、设置虚拟机主机名称 5、绑定主机名和…

【android 9】【input】【11.发送普通motion事件1——touch设备的加载——MultiTouchInputMapper】

系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 …

传知代码-CENet及多模态情感计算实战(论文复现)

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 一、概述 本文对 “Cross-Modal Enhancement Network for Multimodal Sentiment Analysis” 论文进行讲解和手把手复现教学&#xff0c;解决当下热门的多模态情感计算问题&#xff0c;并展示在MOSI和MOSEI两个数…

labview经验分享1-任意16进制字符类型匹配

系列文章目录 1、任意16进制字符类型匹配 文章目录 系列文章目录问题导入实现任意16进制字符类型匹配在这里插入图片描述 总结 问题导入 labveiw的字符串匹配&#xff0c;使用的是正则表达式&#xff0c;可以让我们很方便的对字符串进行字符处理操作。 但是某些情况下&#…

WEB渗透Bypass篇-常规操作

绕过lsa-protection https://github.com/RedCursorSecurityConsulting/PPLKillerLinux绕过disable_function LD_PRELOAD linux环境 putenv()、mail()可用 https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD http://192.168.0.107/bypass_disablefunc.p…

一篇文章教你搭建一个高深莫测的SQL优化器

❓在数据库操作中&#xff0c;SQL优化一直是一个让人头疼的问题。今天&#xff0c;我将教你一种无需编写任何代码&#xff0c;只需要两个组件&#xff0c;便能轻松搭建一个高深莫测的SQL优化器的方法。通过这个方法&#xff0c;它可以将巨慢无比的SQL&#xff0c;把速度优化到极…

重启人生计划-浮舟沧海

&#x1f973;&#x1f973;&#x1f973; 茫茫人海千千万万&#xff0c;感谢这一刻你看到了我的文章&#xff0c;感谢观赏&#xff0c;大家好呀&#xff0c;我是最爱吃鱼罐头&#xff0c;大家可以叫鱼罐头呦~&#x1f973;&#x1f973;&#x1f973; 如果你觉得这个【重启人生…

VIM复合命令

VIM提供了很多 复合命令&#xff0c;可以把两个动作合并为一次按键。极大提高了编辑效率。以下是一些具体的例子&#xff1a; 复合命令等效的长命令说明Cc$删除光标到行尾scl删除光标位置的字符S^C删除整行I^i光标移动到行首A$a光标移动到行尾oA 回车光标下方开启一行Oko光标…

一文掌握SOP搭建步骤方法

如果你正在阅读这篇文章&#xff0c;那么你很可能在寻找如何为你的企业编写标准操作程序&#xff08;SOP&#xff09;的指导&#xff0c;以确保更好的流程被传达给你的团队并且得到遵循。 为什么SOPs很重要 SOPs必须清晰地传达你的业务流程&#xff0c;以标准化操作并确保盈利性…

Vue2 消息订阅与发布

1.pubsub-js 第三方库实现 实现任何框架的消息订阅发布 npm i pubsub-js <template><div class"student"><h2>展示学生的名称:{{ name }}</h2><h2>展示学生的性别:{{ sex }}</h2></div> </template><script>…

浏览器插件利器--allWebPluginV2.0.0.16-Stable版发布

allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX控件直接嵌入浏览器&#xff0c;实现插件加载、界面显示、接口调用、事件回调等。支持Chrome、Firefo…

ollama使用llama3.1案例

ollama安装和运行llama3.1 8b conda create -n ollama python3.11 -y conda activate ollama curl -fsSL https://ollama.com/install.sh | sh ollama run songfy/llama3.1:8b 就这么简单就能运行起来了. 我们可以在命令行中与他交互. 当然我们也可以用接口访问: curl http:…

在IDEA中用自带的数据库 连接 redis 失败(JedisAccessControlException)

文章目录 1、问题出现的背景2、分析问题出现的原因3、解决办法不用输入用户名直接输入密码即可 1、问题出现的背景 redis.clients.jedis.exceptions.JedisAccessControlException: WRONGPASS invalid username-password pair or user is disabled.2、分析问题出现的原因 查看…

智慧水务项目(六)PyScada学习一,初步建立项目并测试

一、说明 Pyscada是scada的python实现&#xff0c;需要学习一下&#xff0c;以备不时之需&#xff0c;目前我的想法是用他来模拟opc数据&#xff0c;毕竟我准备做的项目需要系统与scada通过opc进行通信&#xff0c;正好做一个简单的scada系统 是一个开源的SCADA&#xff08;S…

记录|C#主界面设计【Web风格】

目录 前言一、页面效果二、布局设计2.1 左边菜单栏搭建框架Step1. panelMenu &#xff1a;Step2. panelLogoStep3. button模板Step4. 复制buttonStep5. 微调Button 2.2 界面颜色变换Step1. ThemeColor类Step2. From1.csStep3. 更换按钮点击颜色效果 2.3 按钮点击事件2.4 顶部ti…