• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2025-11-10 16:06 Aet 隐藏边栏 |   抢沙发  1 
文章评分 1 次,平均分 5.0

共享内存

概述

  1. 核心在于让多个进程能够访问同一块物理内存区域,从而实现快速的数据共享
  2. 原理
    1. 将同一段物理内存映射到多个进程各自的虚拟地址空间
  3. 通信方式
    1. 进程直接读写该内存区域,无需内核在用户态和内核态间拷贝数据
  4. 高性能原因
    1. 避免了数据在用户态和内核态之间的拷贝,是速度最快的IPC方式之一
  5. 关于同步
    1. 不提供内置同步机制,需结合信号量等机制确保数据一致性

同步

  1. 互斥量
  2. 事件
  3. 信号量

示例

  1. server

  1. client

文件内存映射

概述

  1. 能将磁盘上的文件直接映射到进程的虚拟地址空间,从而可以像访问内存一样直接读写文件

原理

  1. 建立映射关系
    1. 调用 MapViewOfFile时,操作系统并非立即将整个文件内容加载到物理内存(RAM)中
    2. 它只是在你的进程的虚拟地址空间中划出一块区域,并将这块区域的“后备存储”指定为磁盘上的那个文件
    3. 此时,这部分虚拟地址大部分还没有对应的物理内存
  2. 按需调页
    1. 当首次通过返回的指针访问这块映射区域时,CPU会发现该虚拟地址对应的物理页面不存在,这会触发一个页面错误
    2. 操作系统捕获到这个错误后,会分配一页物理内存,然后从磁盘文件的对应偏移处读取相应的数据(例如4KB)填充该页面,最后更新页表,建立虚拟地址到物理地址的映射
    3. 此后,程序就能像访问普通内存一样访问这些文件数据了
  3. 透明缓存与回写
    1. 被访问过的文件内容会暂时缓存在物理内存中
    2. 如果后续再次访问相同数据,且缓存未被置换,则速度极快
    3. 你修改了映射内存中的数据后,操作系统会负责在适当时机(如系统空闲、调用 FlushViewOfFile或视图解除映射时)将这些脏页面写回磁盘文件,从而保持数据同步

同步

  1. 互斥量
  2. 事件
  3. 信号量

示例

  1. 写入

  1. 读取

对比共享内存

特性维度 共享内存 文件内存映射
实现基础与后端存储 主要依赖系统页文件物理内存,对进程透明 显式地将一个普通文件映射到内存
数据持久化 通常非持久化,进程结束、系统重启后数据丢失 支持持久化,修改可写回磁盘文件,实现数据持久储存
创建 LinuxshmgetWindowsCreateFileMapping(传入特定参数) LinuxmmapWindowsCreateFileMapping(映射真实文件句柄)
通信媒介本质 直接共享物理内存页,速度极快,是最高效的IPC方式之一 以映射的文件为中介,进程通过映射同一文件通信
场景 高性能、大数据量、实时性要求高的进程间通信,如数据库、科学计算 大文件随机读写(提高I/O效率)、需数据持久化的进程间通信

命名管道

概述

  1. 模型
    1. 命名管道采用经典的客户端-服务器模型
    2. 服务器进程负责创建管道并给它分配一个唯一的名字,然后等待客户端的连接请求
    3. 任何知道该管道名称的客户端进程都可以尝试连接
  2. 命名规范
    1. 管道的名称遵循UNC路径格式:\\服务器名\pipe\管道名
    2. 对于本地通信,服务器名通常用一个小数点表示,即\\.\pipe\MyPipeName
    3. 这个名称在整个系统内是唯一的,是客户端找到服务器的关键
  3. 与匿名管道的区别
    1. 匿名管道通常只能在有继承关系(如父子关系)的进程间通信,且是临时的
    2. 命名管道允许任意进程(无论是否有亲缘关系)通过名称进行连接,并且支持跨网络通信(远程服务器名可以是IP地址或主机名)
  4. 内核对象
    1. 管道实例本质上是一个由内核管理的对象
    2. 操作系统会为每个管道实例维护输入和输出缓冲区,用于临时存放待读取或待发送的数据,从而协调读写双方可能存在的速度差异
  5. 模式
    1. 字节模式:数据被视为一个连续的字节流。读取方无法直接感知消息的边界,可能需要自行解析协议
    2. 消息模式:数据被分割成一个个独立的消息单位。每次写入操作构成一条完整的消息,读取操作也会一次读取整条消息,保证了消息边界的完整性

同步

  1. 互斥量
  2. 事件
  3. 信号量

示例

  1. server

  1. client

消息队列

概述

  1. Windows操作系统本身是消息驱动的
    1. 当用户进行输入(如点击鼠标、敲击键盘)或系统事件发生时,这些操作会被转换成消息
    2. 系统会将消息投递到目标窗口所在线程的消息队列
    3. 每个拥有窗口的线程都会维护自己的消息队列,应用程序通过一个持续的消息循环从队列中取出消息,并将其分发给相应的窗口过程函数进行处理
  2. 核心点
    1. 每个消息都包含一个标识符(Msg)和两个附加参数(wParamlParam),可用于携带简单的指令或数据
  3. API
    1. PostMessage/ PostThreadMessage
    2. SendMessage
  4. 为何它适用于进程间通信?
    1. 消息队列能够用于IPC,是因为这些队列由系统内核维护
    2. 不同进程的线程,只要能够获取到目标窗口的句柄(HWND)或线程ID(DWORD),就可以通过上述API向该线程的消息队列发送消息,从而实现跨进程通信

WM_COPYDATA

  1. 传递数据
    1. 由于 wParamlParam只能传递简单数据或值,直接传递指针是无效的(因为进程内存空间隔离)
    2. 为了安全地传递数据块,Windows提供了 WM_COPYDATA 消息
    3. 用这种方式时,发送方填充一个COPYDATASTRUCT结构体,其中包含指向数据的指针和大小
      系统会在内部将数据复制到接收进程的地址空间,接收方在处理WM_COPYDATA消息时,就可以安全地访问这份数据副本

对比选择

  1. 极致的速度、最低的延迟
    1. 例如在实时数据处理、高频交易系统中,共享内存配合信号量等同步机制是毋庸置疑的选择,尽管它实现最复杂
  2. 父子进程、简单数据
    1. 匿名管道因其极致的简单性是一个非常流行的选择
  3. 相互独立的进程
    1. 命名管道、消息队列或共享内存才是可选项
  4. 同步异步
    1. 管道是典型的同步通信:写入方通常需要等待读取方取走数据
    2. 消息队列天然支持异步:生产者放入消息后即可返回,消费者可以后续处理
  5. 数据量
    1. 海量数据交换,优先考虑共享内存
    2. 仅仅是通知某个事件发生,一个信号或许就足够了

进程同步方式

  1. 内核对象
    1. 互斥量
    2. 事件
    3. 信号量
  2. 用户模式
    1. 文件锁:LockFileEx
    2. 条件变量 :CONDITION_VARIABLE
    3. 自旋锁:使用CRITICAL_SECTION

线程通信方式

  1. 全局变量、共享内存
    1. 可直接传递复杂数据
  2. 消息机制
    1. 依赖消息循环
    2. 可通过 wParamlParam参数传递简单数据或指针
  3. 事件
    1. 主要用于通知事件发生,本身不直接传递数据

线程同步方式

  1. 内核对象
    1. 互斥量
    2. 事件
    3. 信号量
  2. 用户模式
    1. 临界区
    2. 条件变量
    3. InterLocked系列函数
  3. C++的方式
    1. std::mutex等:C++对互斥锁的标准实现,常与std::lock_guard配合使用
    2. std::atomic:提供跨平台的原子操作,替代Interlocked函数
    3. std::condition_variable:与std::unique_lock配合,用于等待特定条件成立,实现复杂同步模式

其他

文件锁

条件变量

自旋锁

博客

  1. 进程通信相关
  2. 线程同步相关

声明:本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享