2. 操作系统基础回顾
在多 OS 共享内存与跨域通信的工程实践中,所有上层机制最终都要落到操作系统的资源抽象与隔离能力上。共享内存之所以“共享”,是因为多个执行体映射了同一段物理页;跨域通信之所以“跨域”,是因为数据要跨越进程边界、地址空间边界,甚至 CPU 特权级边界。理解进程、线程、虚拟内存、地址空间、内核态与用户态、系统调用与中断,是后续设计共享内存布局、同步协议和通信通道的前提。
一、进程与线程:资源容器与执行单元
进程是操作系统进行资源分配的基本单位,它拥有独立的虚拟地址空间、文件描述符表、信号处理表、凭证信息等。线程是 CPU 调度的基本单位,同一进程内的多个线程共享地址空间和大部分资源,但各自拥有独立的栈、寄存器上下文和线程局部存储。这个区别直接决定了共享内存的访问方式:同一进程内的线程可以直接通过指针访问共享段;不同进程之间则必须先把同一物理内存映射到各自的虚拟地址空间,再通过约定的偏移或句柄来访问。
在跨域通信中,常见模型是“一个进程内多线程 + 多进程共享内存”。线程负责并发处理,进程负责隔离与容错。若把共享内存放在进程私有堆上,其他进程无法直接访问;只有通过 shm_open、mmap、CreateFileMapping 等系统调用建立共享映射,才能形成跨进程可见的内存区域。
二、虚拟内存与地址空间:隔离与映射
虚拟内存为每个进程提供独立的连续地址空间幻觉。CPU 发出的地址是虚拟地址,经 MMU 和页表翻译为物理地址。页表项中记录物理页框号、权限位、有效位、脏位等。虚拟内存带来的关键能力包括:进程隔离、按需分页、内存映射文件、写时复制以及共享映射。
共享内存的本质,就是让多个进程的页表项指向同一组物理页框。每个进程看到的虚拟地址可以不同,但物理页相同。因此,跨进程共享内存不能传递“裸指针”,而应传递“共享段标识 + 偏移量”。这也是很多跨域通信框架使用句柄、偏移和描述符而不是绝对地址的原因。
三、内核态与用户态:特权级边界
现代 CPU 通常划分多个特权级,操作系统运行在内核态,应用程序运行在用户态。用户态不能直接执行特权指令、访问内核页表或操作硬件寄存器;一旦需要访问硬件、创建映射、发送信号或进行进程间同步,就必须通过系统调用陷入内核。内核态与用户态的切换是有成本的,包括保存上下文、切换栈、检查参数、执行内核代码、返回用户态等。
共享内存的高效性正来自“建立映射后,数据读写不再陷入内核”。但同步、通知和生命周期管理仍可能需要系统调用或原子指令。工程上要区分“数据面”和“控制面”:数据面尽量在用户态完成,控制面才使用系统调用。
四、系统调用与中断机制
系统调用是用户态程序请求内核服务的标准入口。以 Linux 为例,应用通过 syscall 指令或库函数封装进入内核,内核根据系统调用号分发到具体处理函数。常见与共享内存相关的系统调用包括:
shm_open() 创建或打开 POSIX 共享内存对象
ftruncate() 设置共享内存对象大小
mmap() 将共享内存映射到进程地址空间
munmap() 解除映射
msync() 同步映射页到存储
shm_unlink() 删除共享内存对象
中断则分为硬件中断、软件中断和异常。硬件中断由外设触发,例如网卡收到数据包;软件中断常用于系统调用和延迟处理;异常包括缺页、除零、非法指令等。缺页异常与虚拟内存密切相关:当进程访问尚未映射或尚未调入的页时,CPU 触发缺页异常,内核负责分配物理页、读取文件或建立共享映射。共享内存首次访问时也可能触发缺页,把共享段页框映射进当前进程页表。
五、案例场景:两个进程通过共享内存通信
假设进程 A 是采集进程,进程 B 是分析进程。A 通过 shm_open 创建共享内存对象,ftruncate 设置大小,mmap 映射到自己的地址空间;B 通过同名对象打开并映射。两者页表指向同一组物理页。A 写入数据后,通过信号量或原子标志通知 B;B 读取后回写状态。整个过程数据读写不经过内核拷贝,只有同步和映射管理涉及系统调用。
| 概念 | 作用 | 与共享内存的关系 |
|---|---|---|
| 进程 | 资源分配与隔离单位 | 提供独立地址空间,共享内存需跨进程映射 |
| 线程 | CPU 调度单位 | 同进程线程可直接访问共享段,需注意同步 |
| 虚拟内存 | 地址翻译与隔离 | 使不同虚拟地址映射到同一物理页 |
| 地址空间 | 进程可见的地址范围 | 共享内存需映射进各进程地址空间 |
| 内核态/用户态 | 特权级隔离 | 映射与同步需陷入内核,数据读写可在用户态 |
| 系统调用 | 用户态请求内核服务 | shm_open、mmap、munmap 等建立共享映射 |
| 中断/异常 | 异步事件与错误处理 | 缺页异常触发共享页映射与调入 |
六、常见误区与复盘
- 误区一:把虚拟地址当作跨进程标识。虚拟地址只在当前进程有效,跨进程必须使用共享段 ID 和偏移量。
- 误区二:认为共享内存不需要同步。多个执行体并发读写同一物理页,必须使用信号量、互斥锁或原子操作保护。
- 误区三:忽略缺页与映射时机。首次访问共享页可能触发缺页异常,实时性要求高的场景应提前预热或锁定页面。
- 误区四:混淆线程共享与进程共享。线程共享地址空间,进程共享需要显式映射;两者的生命周期和错误隔离能力不同。
shm_unlink 后,已映射的进程仍可继续访问,直到解除映射;但新进程无法再打开。生命周期管理必须与业务状态机一致,避免悬空引用。