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

news2024/11/15 17:04:39

文章目录

  • 一、进程通信原理(让不同进程看到同一份资源)
  • 二、管道通信
    • 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.coloradmin.cn/o/1948011.html

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

相关文章

论文阅读【检测】:Facebook ECCV2020 | DETR

文章目录 论文地址AbstractMotivation模型框架详细结构小结 论文地址 DETR Abstract 提出了一种将目标检测视为直接集预测问题的新方法。简化了检测pipeline,有效地消除了许多手工设计的组件的需求,例如非最大抑制过程或锚生成,这些组件明…

Windows环境下安装Redis并设置Redis开机自启

文章目录 0. 前言1. 下载 Windows 版本的Redis2. 为 Redis 设置连接密码(可选)3. 启动 Redis4. 设置 Redis 开机自启4.1 将 Redis 进程注册为服务4.2 设置 Redis 服务开机自启4.3 重启电脑测试是否配置成功4.4 关闭 Redis 开机自启(拓展&…

Go语言编程 学习笔记整理 第2章 顺序编程 后半部分

1.流程控制 1.1 条件语句 if a < 5 { return 0 } else { return 1 } 注意&#xff1a;在有返回值的函数中&#xff0c;不允许将“最终的”return语句包含在if...else...结构中&#xff0c; 否则会编译失败&#xff01;&#xff01;&#xff01; func example(x int) i…

docker产生日志过大优化

1、Docker容器启动后日志存放位置 #cat /var/lib/docker/containers/容器ID/容器ID-json.log #echo >/var/lib/docker/containers/容器ID/容器ID-json.log临时清除日志 注&#xff1a;echo一个空进去&#xff0c;不需要重启容器&#xff0c;但如果你直接删除这个日志&…

【ROS2】高级:安全-理解安全密钥库

目标&#xff1a;探索位于 ROS 2 安全密钥库中的文件。 教程级别&#xff1a;高级 时间&#xff1a;15 分钟 内容 背景安全工件位置 公钥材料 私钥材料域治理政策 安全飞地 参加测验&#xff01; 背景 在继续之前&#xff0c;请确保您已完成设置安全教程。 sros2 包可以用来创…

vue3在元素上绑定自定义事件弹出虚拟键盘

最近开发中遇到一个需求: 焊接机器人的屏幕上集成web前端网页, 但是没有接入键盘。这就需要web端开发一个虚拟键盘,在网上找个很多虚拟键盘没有特别适合,索性自己写个简单的 图片: 代码: (代码可能比较垃圾冗余,也没时间优化,凑合看吧) 第一步:创建键盘组件 为了方便使用…

鸿蒙 动态共享包HSP的创建和引用

1.什么是动态共享包HSP HSP&#xff08;Harmony Shared Package&#xff09;是动态共享包&#xff0c;可以包含代码、C库、资源和配置文件&#xff0c;通过HSP可以实现代码和资源的共享。HSP不支持独立发布&#xff0c;而是跟随其宿主应用的APP包一起发布&#xff0c;与宿主应…

arinc664总线协议

AFDX总线协议简介 &#xff08;1&#xff09;AFDX的传输速率高&#xff1a;带宽100MHZ&#xff0c;远远高于其他的类型的航空总线。&#xff08;2&#xff09;AFDX网络的鲁棒性高&#xff1a;AFDX的双冗余备份网络可以在某一个网络出现故障时&#xff0c;仍能正常通讯。 其中…

【C语言】结构体详解 -《探索C语言的 “小宇宙” 》

目录 C语言结构体&#xff08;struct&#xff09;详解结构体概览表1. 结构体的基本概念1.1 结构体定义1.2 结构体变量声明 2. 结构体成员的访问2.1 使用点运算符&#xff08;.&#xff09;访问成员输出 2.2 使用箭头运算符&#xff08;->&#xff09;访问成员输出 3. 结构体…

【NPU 系列专栏 2 -- NVIDIA 的 H100 和 H200 是什么?】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 NVIDIA H100 和 H200 芯片NVIDIA H100 芯片简介NVIDIA H100 主要特点NVIDIA H100 应用场景NVIDIA H100 使用举例NVIDIA H200 芯片简介NVIDIA H200 主要特点NVIDIA H200 应用场景NVIDIA H200 使用举例Summary NVIDIA H100 和 H20…

How do I increase max_new_tokens

题意&#xff1a;怎样增加 max_new_tokens 的值 问题背景&#xff1a; Im facing this error while running my code: 运行代码时遇到如下错误&#xff1a; ValueError: Input length of input_ids is 1495, but max_length is set to 20. This can lead to unexpected…

lua 游戏架构 之 游戏 AI (五)ai_autofight_find_way

这段Lua脚本定义了一个名为 ai_autofight_find_way 的类&#xff0c;继承自 ai_base 类。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读238次。定义了一套接口和属性&#xff0c;可以基于这个基础类派生出具有特定行为的AI组件。例如&…

Vue实现简单小案例

一、创建文件夹 二、引用vue.js <script src"../js/vue.js"></script> 三、准备一个容器 <div id"app"><h1>Hello,{{name}}</h1> </div> 四、创建实例 <script>new Vue({el:"#app", //el用于指…

tcache attack

Tcache Attack tcache让堆利用更加简单&#xff1a; tcache回顾&#xff1a; 在 tcache 中新增了两个结构体&#xff0c;分别是 tcache_entry 和 tcache_perthread_struct&#xff1a; /* We overlay this structure on the user-data portion of a chunk when the chunk is …

数据库概念以及增删改

1.概念 、 2.通用语法 3.数据增删改

mac大文件清理软件哪个好 mac大文件怎么清理 苹果电脑清理软件推荐免费

mac采用固态硬盘技术&#xff0c;数据存储和系统响应速度优势明显&#xff0c;但是mac的内存弊端同样体现在其固态硬盘的技术&#xff0c;导致用户无法通过机械硬盘进行扩充内存。而我们日常使用电脑会产生大量系统垃圾、用户缓存等文件&#xff0c;平时下载的电影和大型游戏安…

通信原理-实验六:实验测验

实验六 实验测验 一&#xff1a;测验内容和要求 测试需要完成以下几个步骤&#xff1a; 配置好以下网络图&#xff1b;占总分10%&#xff08;缺少一个扣一分&#xff09;根据下面图配置好对应的IP和网关以及路由等相关配置&#xff0c;保证设备之间连通正常&#xff1b;占总…

【日常记录】【JS】对一个数组,按照某个字段的值,进行分组

文章目录 1. 前言2. lodash 的分组3. Object.groupBy()参考链接 1. 前言 在开发中&#xff0c;经常会遇到一组数据&#xff0c;要按照某个字段进行分组&#xff0c;这个时候会有很多种方法&#xff0c;可以使用 forEach、reduce、等其他方法 reduce 方法 function groupBy(arr…

JS基础知识学习笔记全

JS基础知识学习笔记全 一、引入方式 1、内部脚本 &#xff08;一般定义在body下面会改善执行速度&#xff09; <body></body><!-- 内部脚本 --><script>/* 打开页面警告框显示的内容 */alert(helloJS);</script>2、外部脚本 外部专门新建一…

Redis-主从模式

目录 前言 一.主从节点介绍 二.配置redis主从结构 二.主从复制 四.拓扑结构 五.数据同步 全量复制&#xff08;Full Sync Replication&#xff09; 局部复制&#xff08;Partial Replication&#xff09; Redis的学习专栏&#xff1a;http://t.csdnimg.cn/a8cvV 前言 …