【Linux】管道通信和 system V 通信

文章目录

  • 一、进程通信原理(让不同进程看到同一份资源)
  • 二、管道通信
    • 2.1 管道原理及其特点
    • 2.1 匿名管道和命名管道
  • 三、共享内存通信
    • 3.1 共享内存原理
    • 3.2 创建和关联共享内存
    • 3.3 去关联、ipc 指令和删除共享内存
  • 四、消息队列和信号量(了解)
    • 4.1 消息队列
    • 4.2 信号量
    • 4.3 system V

一、进程通信原理(让不同进程看到同一份资源)

  尽管每个进程在其独立的地址空间中运行,它们之间并没有直接共享的内存区域,但所有进程都共享同一个OS操作系统。这个共享的操作系统会提供相应的进程通信机制,如共享内存、消息传递、信号、管道、套接字等。这些机制允许进程间实现数据的转发和共享,即便它们在内存中没有直接共享的空间。
  因此,进程通信(Inter-Process Communication, IPC)的原理就是:操作系统是所有进程共享的第三方实体,为进程提供了一套丰富的通信工具和协议,使得进程能够在保持独立性的同时,又让不同的进程看到同一份资源!!!以此实现数据的共享和任务的协调。这种设计不仅提高了系统的稳定性和效率,还确保了通信的安全性和有效性,能够管理了进程间的同步和互斥,防止数据在传输过程中发生冲突。

二、管道通信

2.1 管道原理及其特点

  我们把从一个进程连接到另一个进程的一个数据流称为一个 管道,这个数据流就是一段内核缓冲区,默认大小为 4kb,会根据实际情况做适当的调整。也可以叫做伪文件,不会刷新到磁盘。管道的操作形式是基于文件的,它只允许单向通信,如果要双向通信的话,需要建立两个管道,“互相读写”!

管道通信时可能会遇到以下四种情况:

  • 1、读写端正常,管道如果为空,读端就要阻塞
  • 2、读写端正常,管道如果被写满,写端就要阻塞
  • 3、读端正常读,写端关闭,读端就会读到0,表明读到了文件(pipe)结尾,不会被阻塞
  • 4、写端正常写,读端关闭了。操作系统就要向目标文件发送 SIGPIPE(13) 信号,杀掉正在写入的进程


管道通信特点:

  • 1、管道是基于文件的,且文件生命周期是跟随进程的
  • 2、为了保护管道文件的数据安全,进程之间需要进行进程协同,同步与互斥
  • 3、匿名管道,仅允许有血缘关系的进程(常用于父子)进行通信,且是单向通信的
  • 4、是面向字节流的(面向字节流读取, 跟写的时候的格式无关, 读取的时候只跟字节数有关)

2.1 匿名管道和命名管道

匿名管道:int pipe(int pipefd[2]);
  pipefd 数组是一个输入输出型参数,创建成功返回 0,创建失败返回 -1,并设置错误码!pipefd[0] 表示读端文件描述符,pipedf[1] 表示写端文件描述符。匿名管道,只允许有血缘关系的进程之间进行进程间通信,管道是单向通信的,在我们 fork 之后,应让父子进程中的一方写入,另外一方读取。

命名管道:int mkfifo(const char *pathname, mode_t mode);
  创建成功返回 0,失败返回 -1,并设置错误码,参数 pathname 为文件名, mode 是其对应的权限 。命名管道是一种进程间通信机制,它和匿名管道的区别是,它可以让没有血缘关系的进程进行通信。同时命名管道有对应的 inode ,可以理解为磁盘上的伪文件。但它不是一个实际的数据存储文件,无论写入多少数据,其大小都是 0
  命名管道通过路径+文件名作为该文件的唯一标识,用法和普通文件一样,使用 open、write、read 接口,但仍得保持其单向通信的特性,一端读取,一段写入。且需要等待写端打开之后,读端才会打开文件,否则读端会阻塞等待。

三、共享内存通信

3.1 共享内存原理

  共享内存(Shared Memory)是进程间通信的一种方式,它允许两个或多个进程访问同一块物理内存区域。每个进程都有自己的虚拟地址空间,共享区存在于地址空间上的栈区和堆区之间。在用户需要申请共享内存时,操作系统在物理内存中申请一块空间,每个进程在自己的共享区中与这块物理内存单独建立页表映射,这种多个进程共同映射同一块物理内存的操作就叫做共享内存(即让不同的进程,看到同一份资源)
  这些进程可以通过共享区页表映射直接读写这块物理内存,如同访问本进程的私有内存一样,进行数据交互。因此,它是内存级别的通信,没有额外的复制开销,使得通信速度非常快。但由于有多个进程访问同一块空间,也得考虑同步和互斥控制,我们可以通过使用管道、条件变量、消息队列、信号量,加锁等方法来避免竞态条件和数据不一致的问题。

3.2 创建和关联共享内存

从不同角度上来理解实现共享内存的步骤:
操作系统角度:
①创建共享内存
②删除共享内存
进程角度:
③关联共享内存
④去关联共享内存
因此我们需要按①③④②顺序区管理一个共享内存

创建共享内存 int shmget(key_t key, size_t size, int shmflg);
  创建成功返回 shmid,失败返回 -1 ,错误码被设置。size 为要申请的共享内存大小,由于操作系统对内存管理的最小单位是页(4KB),所以 size 建议设置成为页的整数倍。

shmflg为创建共享内存的选项,常见的有两个选项:

  • IPC_CREAT:创建共享内存,不存在,就创建,存在,就获取
  • IPC_EXCL:不单独使用,必须和IPC_CREAT配合使用。如果不存在指定的共享内存,就创建。存在,则出错返回。这样可以保证,如果shmget函数调用成功,一定是一个全新的共享内存。
  • 并且 shmflg 可以按位或文件权限,设置共享内存权限,类似shmget(key,SIZE,flags | 0666);

现在关键的问题来了,你怎么保证不同的进程看到的是同一个共享内存呢?
  操作系统中可能有很多个共享内存在被使用,所以我们就需要用一个唯一值来标识每一个共享内存,即 key 值。在内核中,让不同的进程看到同一份共享内存,让他们拥有同一个 key 即可。

那么这个 key 由谁来提供呢?
  如果由操作系统来提供,那么创建共享内存的进程可以知道 key 值,但是其它要使用这个共享内存的进程如何获取这个 key 呢,不可能让创建共享内存的进程通信给他们吧。因为此时你要解决的就是双方通信的问题,这就变成了鸡生蛋、蛋生鸡的问题。所以 key 值必须由用户之间指定、用户之间的约定的,这样才能确保看到的是同一个共享内存。

获取 key 值:key_t ftok(const char *pathname, int proj_id);
  将文件路径和一个项目标识符,通过一套算法转化为唯一 key 值,这里的路径名和项目标识符就是用户之间约定好的,成功返回一个唯一的 key 值,失败返回 -1,错误码被设置。

  创建共享内存成功返回共享内存标识符 shmid ,它是一个由系统分配的唯一的整数值,用于唯一标识一个共享内存段。key 在操作系统内标定唯一性,而shmid 只在你的进程内,用来表示资源的唯一性!!!接下来的对共享内存的操作(关联、去关联、删除)我们都采用 shmid 作为参数而非 key

关联共享内存:void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

  • shmid:共享内存标识符,由 shmget 函数返回
  • shmaddr:指定附加共享内存的位置。如果设置为 NULL,则由系统选择地址
  • shmflg:控制共享内存的访问方式和其他选项的标志,一般设置为0使用默认行为

  成功返回附加到进程地址空间的共享区的地址。失败,返回 (void *) -1,错误码被设置。使用类似于 molloc,使用时需要强转为需要的指针类型。对此我们就像访问自己的私有内存一样,与其它进程进行数据交互通信

3.3 去关联、ipc 指令和删除共享内存

去关联共享内存:int shmdt(const void *shmaddr);
  成功返回0,失败返回-1,错误码被设置。参数为共享区中 shmat 函数返回的共享区起始地址

ipc 系列相关指令:
一、查看IPC

  • ipcs 和 ipcs -a : 查看所有IPC对象
  • ipcs -q: 查看消息对列对象
  • ipcs -m: 查看共享内存对象
  • ipcs -s: 查看信号量对象

二 、删除 IPC 对象

  • ipcrm -Q key :根据键值key,删除指定的消息对列
  • ipcrm -q id:根据ID,删除指定的消息对列
  • ipcrm -M key:根据键值key,删除指定的共享内存
  • ipcrm -m id:根据ID,删除指定的共享内存
  • ipcrm -S key :根据键值key,删除指定的信号量
  • ipcrm -s id:根据ID,删除指定的信号量

删除共享内存:int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

  • shmid:由 shmget 函数返回的共享内存标识码
  • cmd:将要采取的动作,IPC_STAT:提取 shmid_ds 结构中的数据;IPC_SET:设置 shmid_ds 结构中的数据;IPC_RMID:删除共享内存
  • buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

  毋庸置疑,共享内存可能存在多个必然是要先描述后组织起来的,shmid_ds 就是描述共享内存的结构体。它包括共享内存的大小、创建者和最后操作者的进程ID、当前有多少进程附加到这个共享内存段等属性。顺着往下找,其结构体内部的结构体 ipc_perm 中,就保存了由用户提供的 key 值。

四、消息队列和信号量(了解)

4.1 消息队列

  消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法,读端和写端公用一个队列。当发送方有数据发送时,将数据先打包成一个节点,然后尾插到内核中的消息队列中去。当接收方接收数据时,从队列头部开始去找所需要的节点,然后进行解包得到数据。同时每个数据块都会有个记录类型的数据,来判断该数据块该被哪个进程读取。

消息队列接口:使用几乎和共享内存一样,这里就不详细介绍了
获取:int msgget(key_t key, int msgflg);
控制:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
发送:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
接受:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

4.2 信号量

信号量:
  信号量本质上就是个计数器,它统计的是公共资源资源的剩余量。而公共资源可以被多个进程同时访问,如果访没有同步与互斥,就会导致数据不一致问题(一个进程还在写的时候另一个进程就开始读)。而这种通过同步与互斥被保护起来的资源称为临界资源,访问临界资源的那部分代码称为临界区,其他的代码就称为非临界区。
互斥:当有多个执行流想要访问同一份资源的时候,我们只允许一个执行流进行访问,当这个执行流访问完了,下一个执行流能访问
  进程在访问公共资源前要先申请信号量,需要让多个进程看到同一个计数器,避免资源不足而访问失败。看到同一个计数器说明信号量也是个公共资源,也需要保护,因此对信号量的 pv 操作必须是原子的
P 操作:申请信号量,计数器减 1
V 操作:释放信号量,计数器加 1

原子性:要么不做,要么做完,只有这两种状态的情况

4.3 system V

  进程间通信除了通过管道,都是基于文件的通信方式,还有一种方式是:System V 标准的进程间通信方式。System V 标准是一个在OS层面专门为进程通信设计的一个方案,它是被精心设计过的。system V IPC 提供的通信方式有三种: 共享内存、消息队列和信号量,你可以发现它们的系统调用接口都非常相似,同时它们都有描述自己字段的结构体 xxx_ds。而在它们的 xxx_ds 结构体中,开头都有一个共同的字段 ipc_perm,这个 ipc_perm 类似于基类,被其它子类继承下去

	struct ipc_perm {key_t          __key;    /* Key supplied to shmget(2) */uid_t          uid;      /* Effective UID of owner */gid_t          gid;      /* Effective GID of owner */uid_t          cuid;     /* Effective UID of creator */gid_t          cgid;     /* Effective GID of creator */unsigned short mode;     /* Permissions + SHM_DEST and  SHM_LOCKED flags */unsigned short __seq;    /* Sequence number */};

  因为它们的第一个字段 ipc_perm 是一样的,所以可以维护一个 struct ipc_perm* 的指针数组,存共享内存、消息队列、信号量它们三者中,第一个元素的地址,也就是 &ipc_perm。这样就可以把共享内存、消息队列和信号量三个部分直接管理起来了。而我们知道结构体的第一个成员的地址和结构体对象的地址在数值上是相同的,并且操作系统在内部可以识别这个对象是共享内存、消息队列和信号量中的哪一个,因此我们拿到这个数组中的 ipc_perm 地址便能访问这结构体的其它成员,将它们管理起来

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

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

相关文章

【Unity2D 2022:UI】无法拖拽TextMeshPro组件

在预制体Card上挂载了四个Text Mesh Pro组件,分别命名为Name、HP、ATK、Description。 将预制体Card挂载脚本CardDisplay用来在预制体上显示属性,并创建TextMeshPro对象来接收TextMeshPro组件。 using TMPro; using UnityEngine; using UnityEngine.UI;…

用Redisson写一个库存扣减的方法

使用Redisson来处理库存操作可以确保在高并发环境下库存数据的一致性和完整性。以下是使用Redisson实现库存管理的一些通用方法,包括获取库存、扣减库存、设置库存等。我们将使用Redisson的ReentrantLock来确保并发安全。 首先,确保你已经正确设置了Red…

HCIP之PPP协议(PAP认证,CHAP认证)、GRE、MGRE综合实验

实验过程 一、IP配置 [r1]interface Serial 4/0/0 [r1-Serial4/0/0]ip ad 15.1.1.1 24 [r1]interface GigabitEthernet 0/0/0 [r1-GigabitEthernet0/0/0]ip ad 192.168.1.1 24 r2]interface Serial 4/0/0 [r2-Serial4/0/0]ip ad 25.1.1.2 24 [r2]interface GigabitEthernet 0/…

基于 HTML+ECharts 实现智慧交通数据可视化大屏(含源码)

构建智慧交通数据可视化大屏:基于 HTML 和 ECharts 的实现 随着城市化进程的加快,智慧交通系统已成为提升城市管理效率和居民生活质量的关键。通过数据可视化,交通管理部门可以实时监控交通流量、事故发生率、道路状况等关键指标,…

LabVIEW多种测试仪器集成控制系统

在现代工业生产与科研领域,对测试设备的需求日益增长。传统的手动操作测试不仅效率低下,而且易出错。本项目通过集成控制系统,实现了自动化控制,降低操作复杂度和错误率,提高生产和研究效率。 系统组成与硬件选择 系…

MSSQL注入前置知识

简述 Microsoft SQL server也叫SQL server / MSSQL,由微软推出的关系型数据库,默认端口1433 常见搭配C# / .net IISmssql mssql的数据库文件 数据文件(.mdf):主要的数据文件,包含数据表中的数据和对象信息…

Mongodb入门介绍

文章目录 1、Mongodb:NoSQL数据库,分布式的文档型数据库2、适合场景:3、不适合场景:4、概念5、总结 1、Mongodb:NoSQL数据库,分布式的文档型数据库 2、适合场景: 1、web网站数据存储&#xff…

LeetCode704 二分查找

前言 题目: 704.二分查找 文档: 代码随想录——二分查找 编程语言: C 解题状态: 解答错误,变量定义位置错误。 思路 有序数组的查找,最直接的思路应该就是二分查找。但是在查找的过程中要考虑到区间的边界…

希捷公布第四财季(4FQ24)和整个2024财年的财务业绩

希捷科技控股(Seagate Technology Holdings)公布了截至2024年6月28日的第四财季(4FQ24)和整个2024财年的财务业绩。以下是关键点的总结与分析: ### 4FQ24亮点: - **收入**:18.9亿美元&#xff…

WebRTC与orange pi实现视频画面实时传输

使用WebRTC和Orange Pi可以实现视频画面的实时传输,并将其嵌入到网页中。下面是实现这一功能的一般步骤和所需的组件: 1. 硬件准备 Orange Pi:选择适合的Orange Pi型号(如Orange Pi PC、Orange Pi Zero等)。摄像头&a…

鸿蒙 Navigation VS Router 对比

当前HarmonyOS支持两套路由机制(Navigation和Router),Navigation作为后续长期演进及推荐的路由选择方案,其与Router比较的优势如下: 易用性层面: Navigation天然具备标题、内容、回退按钮的功能联动&…

Springboot循环依赖的解决方式

Springboot循环依赖的解决方式 起因原因解决方案配置文件解决使用工具类获取bean还有一种我设想的方案 起因 今天重构代码时,发现之前的代码结构完全混乱,没有按照MVC分层思想去编写,很多业务逻辑写在了controller中,导致引用的很…

Java | Leetcode Java题解之第278题第一个错误的版本

题目&#xff1a; 题解&#xff1a; public class Solution extends VersionControl {public int firstBadVersion(int n) {int left 1, right n;while (left < right) { // 循环直至区间左右端点相同int mid left (right - left) / 2; // 防止计算时溢出if (isBadVers…

哪个邮箱最安全最好用啊

企业邮箱安全至关重要&#xff0c;需保护隐私、防财务损失、维护通信安全、避免纠纷&#xff0c;并维持业务连续性。哪个企业邮箱最安全好用呢&#xff1f;Zoho企业邮箱&#xff0c;采用加密技术、反垃圾邮件和病毒保护&#xff0c;支持多因素认证&#xff0c;确保数据安全合规…

【大师与bug里特】M_Studio《王国之梦》学习笔记

1️⃣ Object & object(✅) 之辨 《7.泛型事件框架〈余2min左右时〉》 不然inspector窗口的最后一行&#xff08;告诉我们订阅者是SceneLoadManager它身上挂了☝️ObjectEventListener用来监听这个事件 有多少个事件注册到这里来了都能够看到&#xff09;还是不会出现 加上…

开源邮箱套件介绍系列1:SOGo

项目网站&#xff1a;SOGo | Free Open Source Webmail 提示&#xff1a;如下内容大部分来自官方网站&#xff0c;通过AI智能翻译而来。 1. SOGo功能概述 SOGo提供了多种访问日历和消息数据的方式。您的用户可以使用网页浏览器、Microsoft Outlook、Mozilla Thunderbird、Ap…

【java基础】线程状态转化

在Java中&#xff0c;线程有六种基本状态&#xff0c;这些状态反映了线程在其生命周期中的不同阶段。线程的状态转换是由操作系统和JVM&#xff08;Java虚拟机&#xff09;调度机制共同控制的。以下是线程的六种状态及其转换&#xff1a; NEW&#xff08;新建&#xff09; 线程…

create-vue源码学习之 gradient-string 渐变色打印

效果 在使用 create-vue 脚手架时&#xff0c;想实现如下的打印效果。 探究过程 翻到源码里看到这一行 没错&#xff0c;绿色部分就是告诉我们如何生成的。可以看到引入了 gradient-string 包 于是乎&#xff0c;我来试试 pnpm i gradient-string pnpm i --save-dev …

【IT人生系列二】第一次离职你下定了什么决心

本文承接【IT人生系列一】你的第一份工作激起了多少浪花 转眼间&#xff0c;博主已经在java这趟列车上漂流了18个月之久&#xff0c;再美的风景也会厌倦&#xff0c;我也不是那个初到上海充满干劲的少年&#xff0c;理想与现实的落差让我越发迷茫&#xff0c;我无数次想过放弃…

怎么在PPT插入视频?3个做PPT常用的使用技巧分享!

PPT技巧在日常办公中扮演着重要角色&#xff0c;ppt是一个开放的视觉呈现工具和载体&#xff0c;它支持在页面中插入各种内容媒介&#xff0c;包括文本、图片、视频、音频、矢量素材等&#xff0c;特别是当涉及到PPT插入视频时&#xff0c;它的作用就显得尤为突出。 不过说到p…