[Linux] 进程间通信——匿名管道命名管道

标题:[Linux] 进程间通信——匿名管道&&命名管道

@水墨不写bug

(图片来源于网络)

目录

一、进程间通信

 二、进程间通信的方案——匿名管道

(1)匿名管道的原理

(2)使用匿名管道

三、进程间通信的方案——命名管道

(1)认识管道文件

(2)使用命名管道


 正文开始:

一、进程间通信

        当你参加场竞赛,你一定需要与你的队友密切配合,这样才能高效的完成任务。然而,配合的前提是你们知道彼此现在的状况,就是说,你们需要尽可能的共享信息,而这一过程不就是通信吗?于是,得出结论:

        配合的前提是通信。

        一台计算机,是被操作系统管理着(操作系统是软硬件资源的管理者),而进程间由于具有独立性,体现为每个进程都有自己的进程地址空间,这就意味着多个进程之间相互的数据不可见(所有的数据,不论局部或者全局),于是想要实现进程间的相互通信,通常是比较困难的(成本比较高)。

 图1(进程间具有独立性)

        进程间通信的前提是让不同的进程看到同一份资源,这份资源(称为 "共享资源"),不是属于某一个进程,而是操作系统这个中间人提供的资源(本质就是一段内存)。

        由于操作系统不相信任何人,于是操作系统提供了一系列的系统调用,用来创建共享资源。这同时也意味着:操作系统提供了很多接口,调用不同的接口,会创建不同类型的共享资源。这些不同的共享资源就有:

        管道(包括匿名管道、命名管道)SystemV IPC的共享内存、消息队列、信号量

本文要讲解的就是头一种进程间通信的方式:管道

 二、进程间通信的方案——匿名管道

(1)匿名管道的原理

进程打开文件时,发生了什么? 

        当进程以读或者的方式打开一个文件的时候,操作系统会在内存中创建一个结构体——struct file,这个结构体用来维护被打开的文件:

        标准输入,标准输出,标准错误被默认打开(文件结构体数组——fd数组的0,1,2被默认的三个“文件”占用):

图2(进程与被打开的文件对应创建的结构体)

这时,如果进程B以读的方式打开一个文件:比如调用了C的fopen函数以r的方式打开,具体结果就会成为这样:

此时,再以w方式打开同一个文件:

(以读方式打开文件后,再次以写方式打开,会创建一个专门用于write的structfile,但文件的内核级数据会沿用read structfile的同一份)——这是匿名管道的基本原理条件。

        此时fork创建子进程,子进程会“共享”父进程的代码和数据(包括fd_array),于是,可以表示为:

如果让父进程关闭r,子进程关闭w,那么: 这样,不酒满足两个进程看到同一份内核级缓冲区了吗?

这就是匿名管道的原理。


(2)使用匿名管道

头文件:<unistd.h>

函数原型:

 使用:

        调用pipe时,传入一个数组类型int [2],将带出两个文件fd,一个是读fd ,下标为[0],一个是写fd,下标为[1]。这两个fd就是匿名管道内核级缓冲区的fd,由于匿名管道没有文件路径和文件名,所以称为“匿名管道”。

 注意:

        匿名管道只能用于进行具有血缘关系的进程之间进行通信,常用于父子进程之间进行通信。

        管道内部,自带进程之间的同步机制。

        管道文件的生命周期随进程。

        管道文件在通信的时候,是面向字节流的,write次数和read次数不是一一匹配的。

        管道的通信模式,是特殊的半双工模式。

使用时,四种特殊情况:

        管道文件为空 && write fd没有关闭,读条件不具备,读进程被阻塞,直到pipe内有数据。

        管道文件被写满 && read fd 不读但是没有关闭,管道已满,写进程被阻塞,直到pipe内有空间。

        管道一直在读 && 写 fd被关闭,读fd读到0,表示读到了文件结尾。

        read fd关闭 ,write fd没有关闭,若再写入数据,write会被OS以SIGPIPE信号终止。


三、进程间通信的方案——命名管道

         由于匿名管道的缺陷是只能让两个具有血缘关系的进程通信,命名管道就是为了解决这样的问题而设计的。

(1)认识管道文件

通过指令 mkfifo + 文件名 可以创建管道文件,文件类型为p:

        这就是管道文件,这样的文件具有确定的路径和文件名,所以就称为“命名管道”。这个文件对应匿名管道的内核级缓冲区。这就意味着,原理虽然和匿名管道不完全相同,但是思路完全一致,这里不再赘述。

(2)使用命名管道

创建管道文件,两种方法:

        1.指令mkfifo + 管道文件名称

        2.使用系统封装后的接口:

头文件:

函数原型: 

返回值就是命名管道的fd;如果创建失败,返回-1,错误吗被设置。 

删除管道文件,两种方法:

        1.指令方法

        rm + 文件名 / unlink + 文件名

        2.使用系统封装的接口

        


        到这里,你或许对管道的使用还有一些迷惑,通过下面的这一个小项目,或许你会对进程间通信的方案——管道有一个更深入的理解:

        项目:简单的sever和client之间的通信,通给C++的封装来尽量简化代码的逻辑,尽可能规范:

namedPipe.h:

​
#pragma once#include<iostream>
#include<unistd.h>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>using std::cout;
using std::endl;const std::string comPath = "./myNamedPipe";enum WHO
{//文件操作符的默认值defaultFd = -1,//标识调用者的身份sever = 0,client = 1,//读取namedPipe的一次读入数据的大小:bytesBaseSize = 4096
};//管理命名管道的类,不同的身份的人调用会产生不同的效果
//serer管理命名管道的生命周期,client只负责使用管道
class NamedPipe
{
private:bool OpenNamedPipe(int openstyle){//把得到的fd交给成员变量_fd即可_fd = open(comPath.c_str(),openstyle);if(_fd < 0){perror("open namedpipe fail!");exit(1);}return true;}
public:NamedPipe(const std::string& path,int who):_pipePath(path),_who(who),_fd(defaultFd){//sever需要创建命名管道//cilent则什么都不需要做if(who == sever){int res = mkfifo(comPath.c_str(),0666);if(res < 0){perror("sever mkfifo fail!");exit(1);}cout<<"sever mkfifo success!"<<endl;}}bool OpenForRead(){return OpenNamedPipe(O_RDONLY);}bool OpenForWrite(){return OpenNamedPipe(O_WRONLY);}//通过stl的string为载体来写入,s为输入型参数int Write(const std::string &s){int res = write(_fd,s.c_str(),s.size());//如果出错,打印错误信息,终止进程//如果正确,返回写入数据的字节数if(res < 0){perror("read fail!");exit(1);}return res;}//通过stl的string为载体来输出,s为输出型参数int Read(std::string *s){char buf[BaseSize] = {0};int res = read(_fd,buf,BaseSize);*s = buf;//如果出错,打印错误信息,终止进程//如果正确,返回读取的数据的字节数if(res < 0){perror("read fail!");exit(1);}return res;}~NamedPipe(){if(_who == sever){int res = unlink(comPath.c_str());if(res < 0){perror("unlink fail!");exit(1);}if(_fd != defaultFd){close(_fd);}}}private:const std::string _pipePath;//命名管道的路径,便于不同进程找到命名管道int _who;//身份int _fd;//namedpipe的文件描述符
};​

sever.cc:

#include "namePipe.hpp"// 一般可以看着namePipe的头文件来使用头文件内部的接口
// 也就是说
// sever等的enum常量的声明是可以被看到的,所以sever的直接使用并不突兀
// 但是namePipe被写为  hpp = .h + .cc// 服务端,读取数据,管理namedPipe
int main()
{// 通过类来管理namePipe,出作用域自动析构NamedPipe fifo(comPath.c_str(), sever);if (fifo.OpenForRead()){while (true){std::string s;int n = fifo.Read(&s);cout << n << ":" << s.c_str() << endl;}}return 0;
}

client.cc:

#include "namePipe.hpp"// 客户端,写入数据
int main()
{NamedPipe fifo(comPath.c_str(), client);if (fifo.OpenForWrite()){std::string s("I am process A");while (true){sleep(1);int n = fifo.Write(s);cout<<n<<" bytes writen "<<endl;}}return 0;
}

makefile:

.PHONY:all
all:sever client
sever:sever.ccg++ -g -o $@ $^ -std=c++11
client:client.ccg++ -g -o $@ $^ -std=c++11.PHONY:clean
clean:rm -rf sever client

 完~

未经作者同意禁止转载

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一体化数据安全平台uDSP 入选【年度创新安全产品 TOP10】榜单

近日&#xff0c;由 FreeBuf 主办的 FCIS 2024 网络安全创新大会在上海隆重举行。大会现场揭晓了第十届 WitAwards 中国网络安全行业年度评选获奖名单&#xff0c;该评选自 2015 年举办以来一直饱受赞誉&#xff0c;备受关注&#xff0c;评选旨在以最专业的角度和最公正的态度&…

pycharm链接neo4j(导入文件)

1.新建csv文件 2.写入文件 3.运行代码 import csv from py2neo import Graph, Node, Relationship# 连接到Neo4j数据库&#xff0c;使用Bolt协议 graph Graph("bolt://localhost:7687", auth("neo4j", "password"))# 读取CSV文件 with open(…

vscode ctrl+/注释不了css

方式一.全部禁用插件排查问题. 方式二.打开首选项的json文件,注释掉setting.json,排查是哪一行配置有问题. 我的最终问题:需要将 "*.vue": "vue",改成"*.vue": "html", "files.associations": { // "*.vue": &qu…

TCP三次握手与四次挥手(TCP重传机制,2MSL)超详细!!!计算机网络

本篇是关于3次握手和四次挥手的详细解释~ 如果对你有帮助&#xff0c;请点个免费的赞吧&#xff0c;谢谢汪。&#xff08;点个关注也可以&#xff01;&#xff09; 如果以下内容需要补充和修改&#xff0c;请大家在评论区多多交流~。 目录 1. TCP头部&#xff1a; 2. 三次握手…

单片机学习笔记 15. 串口通信(理论)

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

C#中switch语句使用

编写一个程序&#xff0c;使用switch语句将用户输入的分数转换成等级&#xff0c;如表 private static void Main(string[] args) { Console.WriteLine("请输入分数&#xff1a;"); int score int.Parse(Console.ReadLine()); switch (score) …

[网络安全]sqli-labs Less-5 解题详析

[网络安全]Less-5 GET - Double Injection - Single quotes - String:双注入GET单引号字符型注入 判断注入类型判断注入点个数查库名&#xff08;爆破&#xff09; left函数抓包查库名&#xff08;双查询注入&#xff09; 原理实例查库名&#xff08;extractvalue函数&#xff…

pyspark实现基于协同过滤的电影推荐系统

最近在学一门大数据的课&#xff0c;课程要求很开放&#xff0c;任意做一个大数据相关的项目即可&#xff0c;不知道为什么我就想到推荐算法&#xff0c;一直到着手要做之前还没有新的更好的来代替&#xff0c;那就这个吧。 推荐算法 推荐算法的发展由来已久&#xff0c;但和…

python股票数据分析(Pandas)练习

需求&#xff1a; 使用pandas读取一个CSV文件&#xff0c;文件内容包括股票名称、价格和交易量。完成以下任务&#xff1a; 找出价格最高的股票&#xff1b; 计算总交易量&#xff1b; 绘制价格折线图。 代码实现&#xff1a; import pandas as pd import matplotlib.pyplot …

利用Python爬虫精准获取淘宝商品详情的深度解析

在数字化时代&#xff0c;数据的价值日益凸显&#xff0c;尤其是在电子商务领域。淘宝作为中国最大的电商平台之一&#xff0c;拥有海量的商品数据&#xff0c;对于研究市场趋势、分析消费者行为等具有重要意义。本文将详细介绍如何使用Python编写爬虫程序&#xff0c;精准获取…

K8s调度器扩展(scheduler)

1.K8S调度器 筛选插件扩展 为了熟悉 K8S调度器扩展步骤&#xff0c;目前只修改 筛选 插件 准备环境&#xff08;到GitHub直接下载压缩包&#xff0c;然后解压&#xff0c;解压要在Linux系统下完成&#xff09; 2. 编写调度器插件代码 在 Kubernetes 源代码目录下编写调度插件…

领养我的宠物:SpringBoot开发指南

第2章 开发环境与技术 本章节对开发宠物领养系统需要搭建的开发环境&#xff0c;还有宠物领养系统开发中使用的编程技术等进行阐述。 2.1 Java语言 Java语言是当今为止依然在编程语言行业具有生命力的常青树之一。Java语言最原始的诞生&#xff0c;不仅仅是创造者感觉C语言在编…

Permute for Mac 媒体文件格式转换软件 安装教程【音视频图像文件转换,简单操作,轻松转换,提高效率】

Mac分享吧 文章目录 Permute for Mac 格式转换软件 效果图展示一、Permute 格式转换软件 Mac电脑版——v3.11.15⚠️注意事项&#xff1a;1️⃣&#xff1a;下载软件2️⃣&#xff1a;安装软件2.1 左侧安装包拖入右侧文件夹中&#xff0c;等待安装完成&#xff0c;运行软件2.2…

【Android】EventBus的使用及源码分析

文章目录 介绍优点基本用法线程模式POSTINGMAINMAIN_ORDEREDBACKGROUNDASYNC 黏性事件 源码注册getDefault()registerfindSubscriberMethods小结 postpostStickyunregister 介绍 优点 简化组件之间的通信 解耦事件发送者和接收者在 Activity、Fragment 和后台线程中表现良好避…

原子类、AtomicLong、AtomicReference、AtomicIntegerFieldUpdater、LongAdder

原子类 JDK提供的原子类&#xff0c;即Atomic*类有很多&#xff0c;大体可做如下分类&#xff1a; 形式类别举例Atomic*基本类型原子类AtomicInteger、AtomicLong、AtomicBooleanAtomic*Array数组类型原子类AtomicIntegerArray、AtomicLongArray、AtomicReferenceArrayAtomic…

【Electron学习笔记(三)】Electron的主进程和渲染进程

Electron的主进程和渲染进程 Electron的主进程和渲染进程前言正文1、主进程2、渲染进程3、Preload 脚本3.1 在项目目录下创建 preload.js 文件3.2 在 main.js 文件下创建路径变量并将 preload.js 定义为桥梁3.3 在 preload.js 文件下使用 electron 提供的contextBridge 模块3.4…

FFmpeg一些常用的命令

官网&#xff1a;https://ffmpeg.org/ 官网下载&#xff1a;https://ffmpeg.org/download.html 官网下载源码&#xff1a;https://www.ffmpeg.org/releases/ FFmpeg 实用命令 — FFmpeg 教程 文档 一、参数 1.1 FFmpeg 常用参数 参数说明备注-i filename指定输入文件&#…

JAVA篇08 —— String类

欢迎来到我的主页&#xff1a;【一只认真写代码的程序猿】 本篇文章收录于专栏【小小爪哇】 如果这篇文章对你有帮助&#xff0c;希望点赞收藏加关注啦~ 目录 1 String概述 1.1 String特性 1.2 String常用方法 2 StringBuffer类 2.1 String与StringBuffer互转 2.2 Stri…

Flink四大基石之Time (时间语义) 的使用详解

目录 一、引言 二、Time 的分类及 EventTime 的重要性 Time 分类详述 EventTime 重要性凸显 三、Watermark 机制详解 核心原理 Watermark能解决什么问题,如何解决的? Watermark图解原理 举例 总结 多并行度的水印触发 Watermark代码演示 需求 代码演示&#xff…

LabVIEW将TXT文本转换为CSV格式(多行多列)

在LabVIEW中&#xff0c;将TXT格式的文本文件内容转换为Excel格式&#xff08;即CSV文件&#xff09;是一项常见的数据处理任务&#xff0c;适用于将以制表符、空格或其他分隔符分隔的数据格式化为可用于电子表格分析的形式。以下是将TXT文件转换为Excel&#xff08;CSV&#x…