Lec 12 进程间通信

news/2025/1/11 22:03:08/文章来源:https://www.cnblogs.com/mumujun12345/p/18666268

Lec 12 进程间通信

License

本内容版权归上海交通大学并行与分布式系统研究所所有
使用者可以将全部或部分本内容免费用于非商业用途
使用者在使用全部或部分本内容时请注明来源
资料来自上海交通大学并行与分布式系统研究所+材料名字
对于不遵守此声明或者其他违法使用本内容者,将依法保留追究权
本内容的发布采用 Creative Commons Attribution 4.0 License
完整文本

使用多个进程的应用

  • 一些应用程序选择使用不同进程来运行不同模块
    • 优势-1:功能模块化,避免重复造轮子(如数据库、界面绘制)
    • 优势-2:增强模块间隔离,增强安全保障(敏感数据的隔离)
    • 优势-3:提高应用容错能力,限制故障在模块间的传播
  • 然而不同进程拥有不同的内存地址空间
    • 进程与进程之间无法直接进行通信和交互
    • 需要一种进程间通信的方式
      • IPC:Inter-Process Communication

常见IPC的类型

IPC机制 数据抽象 参与者 方向
管道 文件接口 两个进程 单向
共享内存 内存接口 多进程 单向/双向
消息队列 消息接口 多进程 单向/双向
信号 信号接口 多进程 单向
套接字 文件接口 两个进程 单向/双向

IPC接口类型

  • 已有接口
    • 内存接口:共享内存;文件接口:管道(Pipe)、套接字(Socket)
  • 新的接口
    • 消息接口、信号接口等
  • 简单IPC的消息接口
    • 发送消息:Send(message)
    • 接收消息:Recv(message)
    • 远程方法调用:RPC(req_message, resp_message)
    • 回复消息:Reply(resp_message)

简单IPC设计与实现

信息的发送

img

发送者和消费者都依赖于一个通信连接channel,作为媒介进行信息的传输。

信息的接收

img

信息的远程方法调用(receiver)

img

简单IPC的两个阶段

  • 段階1:准备阶段
    • 建立通信连接,即进程间的信道
      • 假设内核已经为两个进程映射了一段共享内存
  • 段階2:通信阶段
    • 数据传递
      • "消息"抽象:通常包含头部(含魔数)和数据内容(500字节)(一般不包含指针)
    • 通信机制
      • 两个消息保存在共享内存中:发送者消息、接收者消息
      • 发送者和接收者通过轮询消息的状态作为通知机制

img

简单IPC数据传递的两种方法

  • 基于共享内存的数据传递
    • 操作系统在通信过程中不干预数据传输
    • 操作系统仅负责准备阶段的映射
    • 优势:无需切换到内核态即可完成IPC(多核场景下),完全由用户态控制,定制能力更强,可实现零内存拷贝(无需内核介入)
  • 基于操作系统辅助的数据传递
    • 操作系统提供接口(系统调用):Send、Recv
    • 通过内核态内存来传递数据,无需在用户态建立共享内存
    • 优势:抽象更简单,用户态直接调用接口,使用更方便,安全性保证更强,发送者在消息被接收时通常无法修改消息,多方(多进程)通信时更灵活、更安全

简单IPC的通知机制

  • 基于轮询(消息头部的状态信息)。
    • 缺点:大量CPU计算资源的浪费。
  • 基于控制流的转移
    • 内核控制进程的运行状态
    • 进程只有在条件满足下进行,避免CPU浪费。

img

简单IPC的方向:单向&双向

  • 简单IPC的一次完整通信过程包含两个方向的通信
    • 发送者传递一个消息(即请求)给接收者
    • 接收者返回一个消息(即结果)给发送者
  • 通信的三种可能方向
    • 仅支持单向通信
    • 仅支持双向通信(可基于单向通信实现)
    • 单向和双向通信均可(根据配置来选择)

IPC控制流:同步&异步

  • 同步IPC
    • IPC操作阻塞进程直到操作完成。
    • 线性控制流。
    • 调用者继续运行时,返回结果已经OK
  • 异步IPC
    • 进程发起IPC操作后,立即返回。无需等待完成。
    • 通过轮询或回调函数(需要内核的支持)来获取返回结果

img

IPC超时机制

  • 一种新的错误:超时
    • 传统的函数调用不存在超时问题
    • IPC涉及两个进程,分别有独立的控制流
  • 超时可能的原因
    • 被调用者是恶意的:故意不返回
    • 被调用者不是恶意的:运行时间过长、调度时间过长、请求丢失等
  • 超时机制
    • 应用可自行设置超时的阈值,但如何选择合适的阈值却很难
    • 特殊的超时机制:阻塞、立即返回(要求被调用者处于可立即响应的状态)

IPC通信连接

  • 方法1:直接通信
    • 通信的一方需要显示地标识另一方,每一方都拥有唯一标识
    • 如:Send(P, message), Recv(Q, message)
    • 连接的建立是自动完成的(由内核完成)
  • 方法2:间接通信
    • 通信双方通过"信箱"的抽象来完成通信
    • 每个信箱有自己唯一的标识符
    • 通信双方并不直接知道在与谁通信
    • 进程间连接的建立发生在共享一个信箱时

IPC权限检查

  • 宏内核
    • 通常基于权限检查的机制实现
    • 如:Linux中与文件的权限检查结合在一起(以后介绍)
  • 微内核
    • 通常基于Capability安全检查机制实现
    • 如seL4将通信连接抽象为内核对象,不同进程对于内核对象的访问权限与操作有Capability来刻画
    • Capability保存在内核中,与进程绑定
    • 进程发起IPC时,内核检查其是否拥有对应的Capability

IPC命名服务

  • 命名服务:一个单独的进程
    • 类似一个全局的看板,协调服务端与客户端之间的信息
    • 服务端可以将自己提供的服务注册到命名服务中
    • 客户端可以通过命名服务进程获取当前可用的服务
  • 命名服务的功能:分发权限
    • 例如:文件系统进程允许命名服务将连接文件系统的权限任意分发,因此所有进程都可以访问全局的文件系统
    • 例如:数据库进程只允许拥有特定证书的客户端连接

IPC小结

img
img

管道(pipe):文件接口的IPC

  • 管道是Unix等系统中常见的进程间通信机制
  • 管道(Pipe): 两个进程间的一根通信通道
    • 一端向里投递,另一端接收
    • 管道是间接消息传递方式,通过共享一个管道来建立连接
  • 例子: 我们常见的命令 ls | grep xxx
  • 优点: 设计和实现简单
    • 针对简单通信场景十分有效
  • 问题:
    • 缺少消息的类型,接收者需要对消息内容进行解析
    • 缓冲区大小预先分配且固定
    • 只能支持单向通信
    • 只能支持最多两个进程间通信
  • 传统的管道缺乏名字,只能在有亲缘关系的进程间使用
    • 也称为“匿名管道”
    • 通常通过fork,在父子进程间传递fd
  • 命名管道:具有文件名
    • 在Linux中也称为fifo,可通过mkfifo()来创建
    • 可以在没有亲缘关系的进程之间实现IPC
    • 允许一个写端,多个读端;或多个写端,一个读端

共享内存(内存接口的IPC)

img
img
img

  • 存在问题
  • 缺少通知机制
    • 若轮询检查,则导致CPU资源浪费
    • 若周期性检查,则可能导致较长的等待时延
    • 根本原因:共享内存的抽象过于底层;缺少OS更多支持
  • TOCTTOU (Time-of-check to Time-of-use)问题
    • 当接收者直接用共享内存上的数据时,可能存在被发送者恶意篡改的情况(发生在接收者检查完数据之后,使用数据之前)
    • 这可能导致buffer overflow等问题

消息传递(message passing)

消息队列

  • 一种消息传递机制
  • 设计选择:
    • 间接通信方式,信箱为内核中维护的消息队列结构体
    • 有(有限的)缓存
    • 没有超时机制
    • 支持多个(大于2)的参与者进行通信
    • 通常是非阻塞的(不考虑如内核缓存区满等异常情况)
  • 消息队列: 以链表的方式组织消息
    • 任何有权限的进程都可以访问队列,写入或者读取
    • 支持异步通信 (非阻塞)
  • 消息的格式: 类型 + 数据
    • 类型:由一个整型表示,具体的意义由用户决定
  • 消息队列是间接消息传递方式
    • 通过共享一个队列来建立连接

img

消息队列:具有类型的消息传递

  • 消息队列的组织
    • 基本遵循FIFO (First-In-First-Out)先进先出原则
    • 消息队列的写入:增加在队列尾部
    • 消息队列的读取:默认从队首获取消息
  • 允许按照类型查询: Recv(A, type, message)
    • 类型为0时返回第一个消息 (FIFO)
    • 类型有值时按照类型查询消息
      • 如type为正数,则返回第一个类型为type的消息

与管道pipe的对比

  • 缓存区设计:
    • 消息队列: 链表的组织方式,动态分配资源,可以设置很大的上限
    • 管道: 固定的缓冲区间,分配过大资源容易造成浪费
  • 消息格式:
    • 消息队列: 带类型的数据
    • 管道: 数据 (字节流)
  • 连接上的通信进程:
    • 消息队列: 可以有多个发送者和接收者
    • 管道: 两个端口,最多对应两个进程
  • 消息的管理:
    • 消息队列: FIFO + 基于类型的查询
    • 管道: FIFO
  • 消息队列更加灵活易用,但是实现也更加复杂

轻量级远程方法调用LRPC

  • IPC会带来较大的性能损失

  • 传统的进程间通信机制通常会结合以下机制:

    • 通知:告诉目标进程事件的发生
    • 调度:修改进程的运行状态以及系统的调度队列
    • 传输:传输一个消息的数据过去
  • 缺少一个轻量的远程调用机制

    • 客户端进程切换到服务端进程,执行特定的函数 (Handler)
    • 参数的传递和结果的返回
  • Lightweight Remote Procedure Call (LRPC)

  • 解决两个主要问题

    • 控制流转换: Client进程快速通知Server进程
    • 数据传输: 将栈和寄存器参数传递给Server进程

img

  • 控制流转换:调度不确定导致时延
  • 控制流转换需要下陷到内核
  • 内核系统为了保证公平等,会在内核中根据情况进行调度
    • Client和Server之间可能会执行多个不相关进程

img

  • 迁移线程: 将Client运行在Server的上下文

  • 为什么需要做控制流转换?

    • 使用Server的代码和数据
    • 使用Server的权限 (如访问某些系统资源)
  • 只切换地址空间、权限表等状态,不做调度和线程切换

  • 数据传输: 数据拷贝的性能损失

  • 大部分Unix类系统,经过内核的传输有(至少)两次拷贝

    • Client \(\to\) 内核 \(\to\) Server
  • 数据拷贝:

    • 慢: 拷贝本身的性能就不快 (内存指令)
    • 不可扩展: 数据量增大10x,时延增大10x

img

  • 共享参数栈和寄存器

  • 参数栈 (Argument stack,简称A-stack)

    • 系统内核为每一对LRPC连接预先分配好一个A-stack
    • A-stack被同时映射在Client进程和Server进程地址空间
    • Client进程只需要将参数准备到A-stack即可
      • 不需要内核额外拷贝
  • 执行栈(Execution stack,简称E-stack)

  • 共享寄存器

    • 普通的上下文切换: 保存当前寄存器状态 → 恢复切换到的进程寄存器状态
    • LRPC迁移进程: 直接使用当前的通用寄存器
      • 类似函数调用中用寄存器传递参数
  • 轻量远程调用:通信连接建立

  • Server进程通过内核注册一个服务描述符

    • 对应Server进程内部的一个处理函数(Handler)
  • 内核为服务描述符预先分配好参数栈

  • 内核为服务描述符分配好调用记录 (Linkage record)

    • 用于从Server进程处返回(类似栈)
  • 内核将参数栈交给Client进程,作为一个绑定成功的标志

    • 在通信过程中,通过检查A-stack来判断Client是否正确发起通信

img

一次调用过程:轻量远程调用

  1. 内核验证绑定对象的正确性,并找到正确的服务描述符
  2. 内核验证参数栈和连接记录
  3. 检查是否有并发调用 (可能导致A-stack等异常)
  4. 将Client的返回地址和栈指针放到连接记录中
  5. 将连接记录放到线程控制结构体中的栈上 (支持嵌套LRPC调用)
  6. 找到Server进程的E-stack (执行代码所使用的栈)
  7. 将当前线程的栈指针设置为Server进程的运行栈地址
  8. 将地址空间切换到Server进程中
  9. 执行Server地址空间中的处理函数
  • 为什么需要将栈分成参数栈和运行栈?
    • 参数栈是为了共享传递参数,而执行栈是为了执行代码已经处理局部变量等使用的
  • LRPC中控制流转换的主要开销是什么?
    • 地址空间的切换(来自硬件限制)是最主要的性能开销
  • 在不考虑多线程的情况下,共享参数栈是否安全?
    • 安全的。因为是同步IPC,所以在被调用者上下文执行的时候,其实没有其他人可以去读写A-stack。

Example: Chcore

  • 通信进程直接切换
    • 启发自LRPC和L4直接切换技术
  • 同步的通信
  • 通过共享内存传输大数据
  • 基于Capability的权限控制
    • 类似Unix文件描述符的权限机制,Capability表示一个线程/进程对于系统资源的具体权限

建立通信连接流程

  • 服务端进程在内核中注册服务
  • 客户端进程向内核申请连接目标服务端进程的服务
    • 可选: 设置共享内存
  • 内核将客户端请求请求转发给服务端
  • 服务端告诉内核同意连接 (或拒绝)
    • 可选: 设置共享内存
  • 内核建立连接,并把连接的Capability返回给客户端
    • 或返回拒绝

发起通信

  • 客户端进程通过连接的Capability发起进程间通信请求
  • 内核检查权限,若通过则继续步骤3,否则返回错误
  • 内核直接切换到服务端进程执行 (不经过调度器)
    • 将通信请求的参数设置给服务端进程的寄存器中
  • 服务端处理完毕后,通过与步骤3相反的过程将返回值传回客户端

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/867927.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

老毛子PADAVAN为何不能挂载大容量移动硬盘/U盘?解决办法竟然如此简单

问题: 我用的是极路由B70,刷了PADAVAN,B70自身带有一个USB2.0,一个USB3.0,插入一个2TB的移动硬盘,NTFS格式,可以识别到,但没有挂载成功。 查看日志, 确实提示挂载失败,然后看到挂载的名字带有一些@@@@@。 解决办法: 联想自己移动硬盘的名字是中文。于是拔下移动硬盘,…

转:python的zmq模块

转自:https://www.jianshu.com/p/04660f746a16https://blog.csdn.net/SweetHeartHuaZai/article/details/1269348191、zmq介绍: 创建和销毁套接字:zmq.socket(), zmq.close() 配置和读取套接字:zmq.setsockopt(), zmq.getsockopt() 为套接字建立连接:zmq.bind(), zmq.conn…

深度强化学习实战:训练DQN模型玩超级马里奥兄弟

深度学习作为当前计算机科学领域最具前沿性的研究方向之一,其应用范围涵盖了从计算机视觉到自然语言处理等多个领域。本文将探讨深度学习在游戏领域的一个具体应用:构建一个能够自主学习并完成超级马里奥兄弟的游戏的智能系统。强化学习基础 强化学习是机器学习的一个重要分支…

导出和导入word样式模板

对于自己配置过之后常用的word样式可以导出作为样式模板, 可以重复使用.举例说一下哪些是常用的word样式, 例如: (常见的): 中文的内容的样式, 中英文分别设置不同的样式 (比较高级的)多级标题自动编号, 题注跟随标题的编号word功能 word自带导出导入模板样式的功能. word样式模…

远程连接和FTP传输问题

遇到的远程连接和FTP传输问题,我们进行了详细调查。以下是我们的分析和建议:远程桌面连接问题:端口检查:默认情况下,Windows系统的远程桌面服务使用的是3389端口。请确保该端口未被防火墙或其他安全软件阻塞。可以通过命令行工具netstat -an | findstr 3389来检查端口状态…

宝塔面板无法登录,如何重置密码并恢复正常访问?

当您遇到宝塔面板无法登录的问题时,可能是由于密码遗忘、凭据错误或服务器配置问题引起的。为了帮助您更好地理解和解决这个问题,以下是几个可能的原因及相应的解决方案:检查凭据是否正确首先,请确认您输入的用户名和密码是否正确。宝塔面板默认的用户名是admin,初始密码通…

网站偶尔无法访问或加载不完全

网站偶尔出现无法访问或无法加载所有网页的情况,这可能是由多种因素引起的。为了帮助您更好地排查和解决问题,建议您按照以下步骤进行检查:检查服务器资源使用情况:首先,确认服务器的带宽、CPU、内存等资源是否正常。可以通过服务器监控工具(如阿里云的云监控)查看实时资…

云服务器登录无权限,Nginx无法启动

服务器系统可能处于只读状态,导致您无法正常登录并修改配置文件。我们已经为您进入单用户模式修复了文件系统,现在Nginx可以正常运行了。非常感谢您长期对我司的支持! 出现这种情况的原因可能是某些程序未正常停止,导致文件系统出现问题。建议您以后多观察服务器的状态,确…

恢复丢失的网站数据及提供后台密码的方法

用户报告称其网站数据已丢失,急需恢复备份的数据库并获取后台管理密码。这种情况严重影响了网站的正常运营,必须尽快解决。 解决方案:确认备份可用性:首先,确认是否有可靠的备份文件可供恢复。检查服务器上的自动备份机制或之前手动创建的备份文件。如果没有现成的备份,询…

如何使用一段代码修改网站内容

在网站开发中,使用一段代码可以快速修改网站内容。以下是详细的步骤和注意事项:确定要修改的内容:确定您要修改的具体内容,例如文本、图片、链接等。 找到包含该内容的HTML文件或模板文件。获取FTP信息:从您的网站托管商处获取FTP登录信息,包括FTP服务器地址、用户名、密…

如何使用织梦CMS(DedeCMS)修改网站

织梦CMS(DedeCMS)是一款广泛使用的开源内容管理系统,适用于各种类型的网站。以下是使用织梦CMS修改网站的详细步骤和注意事项:登录织梦CMS后台:使用管理员账户登录织梦CMS后台。通常,后台地址为域名/dede或域名/admin。 输入用户名和密码,点击“登录”按钮。找到内容管理…