进程间通信的几种方式

article/2025/10/9 13:15:32

一、管道

在Linux 中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现如下所述。

• 限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux 中,该缓冲区的大小为1 页,即4KB,使得它的大小不像文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。

• 读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。

注意,从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

(一)、管道的结构

在Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file 结构和VFS 的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的。如图所示。


图中有两个 file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。

一个普通的管道仅可供具有共同祖先的两个进程之间共享,并且这个祖先必须已经建立了供它们使用的管道。
注意,在管道中的数据始终以和写数据相同的次序来进行读,这表示lseek()系统调用对管道不起作用。

二、信号

(一)、信号在内核中的表示


中断的响应和处理都发生在内核空间,而信号的响应发生在内核空间,信号处理程序的执行却发生在用户空间。
那么,什么时候检测和响应信号呢?通常发生在以下两种情况下:
(1)当前进程由于系统调用、中断或异常而进入内核空间以后,从内核空间返回到用户空间前夕;
(2)当前进程在内核中进入睡眠以后刚被唤醒的时候,由于检测到信号的存在而提前返回到用户空间。

当有信号要响应时,处理器执行路线的示意图如图 所示。


当用户进程通过系统调用刚进入内核的时候,CPU会自动在该进程的内核栈上压入下图所示的内容:


在处理完系统调用以后,就要调用do_signal()函数进行设置frame等工作。这时内核堆栈的状态应该跟下图左半部分类似(系统调用将一些信息压入栈了):

在找到了信号处理函数之后,do_signal() 函数首先把内核堆栈中存放返回执行点的eip保存为old_eip,然后将eip替换为信号处理函数的地址,然后将内核中保存的“原ESP”(即用户态栈地址)减去一定的值,目的是扩大用户态的栈,然后将内核栈上的内容保存到用户栈上,这个过程就是设置frame.值得注意的是下面两点:

1、之所以把EIP的值设置成信号处理函数的地址,是因为一旦进程返回用户态,就要去执行信号处理程序,所以EIP要指向信号处理程序而不是原来应该执行的地址。
2、之所以要把frame从内核栈拷贝到用户栈,是因为进程从内核态返回用户态会清理这次调用所用到的内核栈(类似函数调用),内核栈又太小,不能单纯的在栈上保存另一个frame(想象一下嵌套信号处理),而我们需要EAX(系统调用返回值)、EIP这些信息以便执行完信号处理函数后能继续执行程序,所以把它们拷贝到用户态栈以保存起来。

这时进程返回用户空间,就会根据内核栈中的EIP值执行信号处理函数。那么,信号处理程序执行完后,怎么返回程序继续执行呢?
信号处理程序执行完毕之后,进程会主动调用sigreturn()系统调用再次回到内核,查看有没有其他信号需要处理,如果没有,这时内核就会做一些善后工作,将之前保存的frame恢复到内核栈,恢复eip的值为old_eip,然后返回用户空间,程序就能够继续执行。至此,内核遍完成了一次(或几次)信号处理工作。

三、System V 的IPC 机制

为了提供与其他系统的兼容性,Linux 也支持3 种system Ⅴ的进程间通信机制:消息、信号量(semaphores)和共享内存,Linux 对这些机制的实施大同小异。我们把信号量、消息和共享内存统称System V IPC 的对象,每一个对象都具有同样类型的接口,即系统调用。就像每个文件都有一个打开文件号一样,每个对象也都有唯一的识别号,进程可以通过系统调用传递的识别号来存取这些对象,与文件的存取一样,对这些对象的存取也要验证存取权限,System V IPC 可以通过系统调用对对象的创建者设置这些对象的存取权限。在Linux 内核中,System V IPC 的所有对象有一个公共的数据结构pc_perm 结构,它是IPC 对象的权限描述。

(一)、信号量

Linux 中信号量是通过内核提供的一系列数据结构实现的,这些数据结构存在于内核空间,对它们的分析是充分理解信号量及利用信号量实现进程间通信的基础。

如果进程被挂起,Linux 必须保存信号量的操作状态并将当前进程放入等待队列。为此,Linux 内核在堆栈中建立一个 sem_queue 结构并填充该结构。新的 sem_queue 结构添加到集合的等待队列中(利用 sem_pending 和 sem_pending_last 指针)。当前进程放入sem_queue 结构的等待队列中(sleeper)后调用调度程序选择其他的进程运行。

当某个进程修改了信号量而进入临界区之后,却因为崩溃或被“杀死(kill)”而没有退出临界区,这时,其他被挂起在信号量上的进程永远得不到运行机会,这就是所谓的死锁。Linux 通过维护一个信号量数组的调整列表(semadj)来避免这一问题。其基本思想是,当应用这些“调整”时,让信号量的状态退回到操作实施前的状态。

(二)、消息队列

Linux 中的消息可以被描述成在内核地址空间的一个内部链表,每一个消息队列由一个IPC 的标识号唯一地标识。Linux 为系统中所有的消息队列维护一个 msgque 链表,该链表中的每个指针指向一个 msgid_ds 结构,该结构完整描述一个消息队列。

(三)、共享内存

与消息队列和信号量集合类似,内核为每一个共享内存段(存在于它的地址空间)维护着一个特殊的数据结构shmid_ds

我们用图 来表示共享内存的数据结构shmid_ds 与其他相关数据结构的关系。


某个进程第1 次访问共享虚拟内存时将产生缺页异常。这时,Linux 找出描述该内存的vm_area_struct 结构,该结构中包含用来处理这种共享虚拟内存段的处理函数地址。共享内存缺页异常处理代码对shmid_ds 的页表项表进行搜索,以便查看是否存在该共享虚拟内存的页表项。如果没有,系统将分配一个物理页并建立页表项,该页表项加入 shmid_ds 结构的同时也添加到进程的页表中。这就意味着当下一个进程试图访问这页内存时出现缺页异常,共享内存的缺页异常处理代码则把新创建的物理页给这个进程。因此说,第1 个进程对共享内存的存取引起创建新的物理页面,而其他进程对共享内存的存取引起把那个页添加到它们的地址空间。

当某个进程不再共享其虚拟内存时,利用系统调用将共享段从自己的虚拟地址区域中移去,并更新进程页表。当最后一个进程释放了共享段之后,系统将释放给共享段所分配的物理页。

当共享的虚拟内存没有被锁定到物理内存时,共享内存也可能会被交换到交换区中。

四、Posix 的IPC 机制

信号量:分为命名和匿名信号量。命名信号量通常用于不共享内存的进程之间(内核实现);匿名信号量可以用于线程通信(存放于线程共享的内存,如全局变量),或者用于进程间通信(存放于进程共享的内存,如System V/ Posix 共享内存)。

消息队列、共享内存:与System V 类似。

互斥锁mutex + 匿名信号量:线程通信
互斥锁mutex + 条件变量condition :线程通信

五、进程池的实现思路

    1、父子进程之间使用管道通信,传递任务数据状态等,主进程使用某种算法主动选择子进程。最简单、最常用的算法是随机算法和Round-Robin(轮流选取)算法,但更优秀、更智能的算法将使任务在各个工作进程中更均匀地分配、从而减轻服务器的整体压力。
    2、主进程和所有子进程通过一个共享的工作队列来同步,子进程都睡眠在该工作队列上。当有新的任务到来时,主进程将任务添加到工作队列中。这将唤醒正在等待任务的子进程,不过只有一个子进程获得新任务的“接管权”,它可以从工作队列中取出并执行之,而其他子进程将继续睡眠在工作队列上。

六、线程池的实现思路
       主线程接到任务时将任务添加到全局共享的队列中(可以用链表实现),添加任务前后需要加锁,添加任务内部操作成功时触发成功全局共享的信号量或者条件变量,此时所有子线程都在等待可用信号,wait 成功的线程从任务队列取走任务去执行。
 


http://chatgpt.dhexx.cn/article/YnDoiXb2.shtml

相关文章

【进程间通信】进程间通信方式汇总

个人主页:董哥聊技术 我是董哥,嵌入式领域新星创作者 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 文章目录 1、管道模型1.1 匿名管道1.2 命名管道 2、消息队列2.1 创建消息队列2.2 发送消息2.3 接收消息 3、共…

android中进程间通信的几种方式

进程间通信(IPC)方式 使用Bundle使用文件共享使用Messenger使用AIDL使用COntentProvider使用Socket 一、使用Bundle 我们都知道Android中三大组件Activity,Service,Receiver都支持在Intent中传递Bundle数据,而Bundle…

操作系统——进程间通信

文章目录 其他文章管道消息队列共享内存信号量信号Socket总结 个人博客网站: https://xingkongdiyiren.github.io/myblog/,完整的Java知识体系,包括408,架构,业务,产品,软技能等 其他文章 操作系统——概…

进程间的通信方式(六种)

进程之间的通信 参考文章:https://blog.csdn.net/qq_34827674/article/details/107678226 前提知识:每个进程都有自己的用户空间,而内核空间是每个进程共享的。因此进程之间想要进行通信,就需要通过内核来实现。 管道&#xff1…

【操作系统】进程间通信的五种方式

引言1.进程对白:管道、记名管道、套接字1.管道2.虫洞:套接字3.信号 4.信号旗语:信号量5.进程拥抱:共享内存 引言 进程作为人类的发明,自然免不了脱离人类的习性,也有通信需求。如果进程之间不进行任何通信…

进程之间的通信方式

进程之间的通信方式包括管道,消息队列,共享内存,信号,信号量,socket六种方式,下面来对这6种方式分别进行介绍。 一、管道 管道的结构示意图如上所示,管道包含一个输入端和一个输出端&#xff0…

进程间通信的六种常见方式

目录 进程间通信(IPC): 一、管道 二、FIFO 三、消息队列 四、共享内存 五、信号 六、信号量 七、进程间通信方式总结: 进程间通信(IPC): 进程间通信的方式有很多,这里主要…

idea数据库管理工具配置连接数据库

idea数据库管理工具配置连接数据库 —————————————————————————————————————————————————————— 在cmd中操作数据库太麻烦了,还好idea为我们提供了很方便的数据库管理工具,下面看看如何用idea连接…

idea连接数据库失败解决办法

一.IDEA连接Mysql报错: 未找到驱动程序类 ‘com.mysql.cj.jdbc.Driver‘.  Change driver class 报错详细内容:未找到驱动程序类 ‘com.mysql.cj.jdbc.Driver’. Change driver class 报错原因:Mysql版本为5.0,找不到com.mysql.…

IDEA中如何连接数据库并显示数据库信息。

我的相关博客: java代码程序中连接mysql数据库的方法及代码 mysql数据库并发上锁问题,java代码 关于IDEA中怎么连接mysql数据库 相信部分朋友在使用IDEA操作数据库的时候会出现有关数据库信息的报错。 显示没有此表,或者无数据库等错误信息…

idea连接数据库失败原因及解决方案

这是因为安装mysql的时候时区设置的不正确 mysql默认的是美国的时区,而我们中国大陆要比他们迟8小时,采用8:00格式。使用的数据库是MySQL,在你没有指定MySQL驱动版本的情况下它自动依赖的驱动是8.0.12很高的版本,这是由于数据库和…

IDEA如何连接数据库 / IDEA连接数据库 新手,图解

如果在下面操作中遇到了问题,可以查看我的这篇笔记 https://blog.csdn.net/qq_44627608/article/details/115442815 1. 打开IDEA的数据库设置 2. 新建数据库链接 左上角的便是你链接的数据库了,第一次打开时左上角应该是空的,需要你从下面的…

IDEA使用Database连接数据库

一,连接数据库 1.点击右侧Database后,点击左上角按钮,然后选中Data Source ,无论使用的是MariaDB还是MySQL都选中MySQL 2.点击非常隐蔽的 … 按钮 3.选中你需要使用的数据库 4.填写数据的账号,密码,数据库名称&…

idea代码连接mysql数据库操作

此文章仅为作者学习上的问题记录,如有错误,欢迎指正。 首先是准备工作 先创建一个Module 之后在此Module下创建一个lib包 然后将下载的连接包复制到lib包下,连接包下载地址: https://cdn.mysql.com//Downloads/Connector-J/mysq…

IDEA中配置数据库连接

1.点击IDEA右边框的 Database ,在展开的界面点击 选择 Data Source ,再选择 MySQL 2.在弹出的界面进行基本信息的填写 3.填写完后,点击Test Connection 测试一下 这样就是填写的没问题,如果是第一次点击这个,需要下载…

Idea连接MySQL数据库教程 (简单明了)

使用Idea连接数据库 具体步骤:点击右侧DataBase → 点击号 → 点击Data Source 选择MySQL → 输入用户名、密码、连接的数据库名称(连接路径会自动生成) → 可点击下面的Test Connection来测试连接 注意事项一: 第一次连接需要下…

IntelliJ IDEA配置连接MySQL数据库

如图: 1、点击主界面右侧边栏Database 2、点击""号 3、点击Data Source 4、点击MySQL 如图填写数据库名,用户名和密码,之后点击下方Test Connection测试 连接成功会显示上图字样 这时发现已经可以查看到数据库信息,说…

IDEA连接mysql数据库

1.保证mysql数据库和IDEA安装成功后,找到IDEA中mysql数据库的连接方式 按照图上的顺序 2.配置连接 在第一次使用的时候,除了要配置连接,还有配置相应的驱动,否则连接的时候会报错。 图中的①②③④⑤分别表示为: ①…

如何使用IDEA连接数据库?

一定要下载IDEA专业版 之前我一直用的是社区版,有诸多内容限制,且无直连数据库功能 通过学生认证(我是认证失败直接找某宝了)直接可以获得1年的免费试用时长 具体操作 然后输入用户、密码、数据库 这里值得一说的是&#xff0c…

Idea连接数据库并执行SQL语句

1、Idea显示Database 2、连接数据库 1、打开界面 2、配置连接信息 3、测试连接 4、面板基本信息 5、选择要显示的数据库 6、表的基本信息 7、新建查询 8、设置sql的备注名称 9、编写sql执行 10、执行结果 3、连接可能出现的问题 1、Idea显示Database idea显示Data…