2.6 进程通信
L11
学习目标
进程通信用消息队列传递数据;本课对照 LiteOS-M 的 LOS_Queue,在内核里实现发送/接收与阻塞唤醒。
- 直接通信与间接通信(信箱/队列)是高级通信的两类实现。
- 消息队列:发送方把消息写入队列,接收方读出;队列满/空时任务阻塞,由内核唤醒。
- MCU 上多任务共享地址空间,仍可用队列做解耦,避免直接共享可变缓冲。
- 本课重点阅读 kernel/los_queue.c:Create / Write / Read 与等待队列。
原理 · 深入理解
2.6 进程通信
本节概览:先建立「进程通信」的框架,再依次展开下列小节。
- 进程通信的类型
- 消息传递通信的实现方式
- 直接消息传递系统实例
进程间传送大量数据时,应利用 OS 提供的高级通信工具。
- 高效地传送大量数据:用户可直接利用高级通信命令高效传送
- 使用方便:OS 隐藏通信细节,提供一组通信原语,通信过程对用户透明
2.6.1 进程通信的类型
消息传递系统中,进程不再借助共享存储区或数据结构,而是以格式化的消息(message)为单位,把数据封装在消息中,通过 OS 提供的通信原语在进程间传递,完成数据交换。
它属于高级通信方式,按实现方式不同分为两类。
- 直接通信方式
- 间接通信方式
管道(pipe)是用于连接一个写进程和一个读进程以实现通信的共享文件,又称 pipe 文件。
写进程以字符流形式把大量数据送入管道,读进程从管道中读出数据,因此称为管道通信。为协调双方,管道机制必须提供三方面协调能力。
- 互斥:一个进程对 pipe 读写时,其它进程必须等待
- 同步:写满一定数量(如 4KB)后写进程睡眠,读走后再唤醒;读空 pipe 时也睡眠等待
- 确定对方是否存在,只有对方存在才能进行通信
远程过程调用(RPC)的执行步骤分两段:客户端发起调用、服务器端执行并返回结果。
客户端先调用本地客户存根,存根打包消息后由本地客户进程发往远程服务器;服务器进程收到消息后转给对应服务器存根,由存根拆包并调用实际过程。
本地调用者以一般方式调用远程过程在本地关联的客户存根,传递参数后将控制权转给客户存根。
客户存根建立含过程名和调用参数的消息,将控制权转回本地客户进程。
服务器存根从消息取出参数,以一般方式调用服务器上关联的过程。
共享存储器系统中,相互通信的进程共享某些数据结构或共享存储区,借此进行通信。按共享对象的不同分为两类。
基于共享数据结构的通信方式较简单,传送数据量小;基于共享存储区的通信方式则在内存中划出一块共享区,进程以高速传送大量数据。
- 基于共享数据结构的通信方式
- 基于共享存储区的通信方式
客户机-服务器系统中常采用套接字(Socket)和远程过程调用(RPC)实现通信。
套接字基于本地文件系统支持时,通信双方通过对一个特殊文件的读写完成通信;基于网络型时则用于跨主机通信。RPC 是一个通信协议,允许一台主机上的进程调用另一台主机上的进程,对程序员表现为常规过程调用。
套接字关联到一个特殊文件,通常采用非对称方式通信,发送者需提供接收者命名。
用于通过网络连接的系统,支持远程过程调用和远程方法调用。
通信双方通过对特殊文件的读写实现通信,可基于本地文件或网络。
远程过程调用协议,使本地进程能调用远程主机上的进程,面向对象语境下亦称远程方法调用。
远程过程调用(RPC)的执行过程:客户端调用本地存根,存根将参数打包为消息发送给服务器;服务器存根解包并调用远程过程,完成后将结果打包返回。
- 服务器端远程过程运行完毕后将结果返回给服务器存根
- 服务器存根将结果打包为消息并转移控制权给远程服务器进程
- 远程服务器进程将消息发送回客户端
2.6.2 消息传递通信的实现方式
消息传递系统中所传递的消息必须有一定格式。可采用变长消息格式,即进程发送消息的长度可变;变长消息在处理和存储上开销更大,但方便了用户。
除格式外,还要考虑进程同步方式和通信链路两类问题。发送或接收完成后,进程可继续运行也可阻塞,需由同步机制协调。
发送或接收完成后,进程或者继续发送(接收)或者阻塞,需要同步机制协调通信。
为在发送与接收进程间通信而建立的链路,可显式建立后再拆除,也可由系统在发送时自动建立。
只允许发送进程向接收进程发送消息,或反之。
既允许 A 向 B 发送,也允许 B 同时向 A 发送。
信箱是一种数据结构,逻辑上分为信箱头和信箱体两部分。
信箱头存放信箱的描述信息,如标识符、拥有者、口令、空格数等;信箱体由若干可存放消息的信箱格组成,格数与每格大小在创建时确定。系统为信箱通信提供创建、撤销、发送、接收等原语。
- 信箱头:存放信箱标识符、拥有者、口令、空格数等描述信息
- 信箱体:由若干信箱格组成,格数和每格大小在创建时确定
信箱通信原语:Send(mailbox,message) 发送消息到指定邮箱;Receive(mailbox,message) 从指定邮箱接收消息。
邮箱可由操作系统或用户进程创建,创建者即拥有者,据此分为私用邮箱、公用邮箱、共享邮箱三类。
系统为邮箱通信提供了若干条原语,用于邮箱的创建、撤销和收发消息。邮箱可由 OS 或用户进程创建,据此分为三类。
- 邮箱的创建和撤销
- 私用邮箱
- 公用邮箱
- 共享邮箱
系统为邮箱通信提供了若干条原语,分别用于。
邮箱可由操作系统创建,也可由用户进程创建,创建者是邮箱的拥有者。据此,可把邮箱分为以下三类。
- 邮箱的创建和撤销
- 私用邮箱
- 公用邮箱
- 共享邮箱
信箱通信原语
信箱的类型
消息的发送和接收。 Send(mailbox,message); 将一个消息发到指定邮箱 Receive(mailbox,message); 从指定邮箱中接收一个消息
2.6.3 直接消息传递系统实例
消息缓冲队列通信方式中,主要的数据结构是消息缓冲区;进程的 PCB 中也要增加若干通信数据项,用于管理消息队列并实现同步。
PCB 中需设置消息队列队首指针,以及用于互斥的信号量 Mutex 和表示消息资源的信号量 sm,配合消息缓冲队列完成消息的发送与接收。
消息缓冲队列通信方式中的主要数据结构,用于暂存待传递的消息。
包括消息队列队首指针、互斥信号量 Mutex、资源信号量 sm。
接收进程调用接收原语 receive(b),从自己的消息缓冲队列 mq 中摘下第一个消息缓冲区 i。
随后将该缓冲区中的数据复制到以 b 为首址的指定消息接收区,完成一次消息接收。
接收进程调用接收原语 receive(b),从自己的消息缓冲队列 mq 中摘下第一个消息缓冲区 i,将数据复制到以 b 为首址的接收区。
做 · 实验
- 打开工作台 L11,默认进入 los_queue.c,对照 Write/Read 如何阻塞与唤醒。
- 烧录 18-queue,确认 send/recv 编号一致。
- 根据「学习目标」列出 3 个关键词,对照原理段落解释给自己听。
进程通信
用控件单步操作;日志区记下每一步发生了什么。
代码导读
本课在工作台编译烧录。先读 user 实验入口,再对照 kernel / HDF 中被调用的函数,不要一上来改链接脚本。
总结与提升
- 能区分同步机制与消息通信。
- 能指出队列满/空时任务落在哪个等待结构。
- 能用自己的话复述学习目标,并指出概念与板上实验的对应。
延伸思考
- 若引入用户态线程,消息队列应放内核还是用户库?
- 如果把本节机制拿到 Linux 或完整 OpenHarmony 上,会多出哪些硬件假设?