Files
Operating-System/Homework/04第四章 互斥同步与通讯(2).md
2026-06-25 00:09:09 +08:00

26 KiB
Raw Permalink Blame History

第四章 互斥、同步与通讯(下)

一、知识讲解

本章承接第四章(上)的内容,重点介绍进程高级同步机制(会合)进程高级通讯机制。低级同步(信号灯、管程)基于共享存储区,难以适应分布式环境;高级机制则更适合分布/异构系统。


4.3.6 会合Rendezvous

1. 会合的引入背景

Ada 语言80年代由美国国防部 DOD 发起Ada 95 引入面向对象特性)首先提出"会合"作为分布式系统中的同步通讯机制。

  • 共享变量方式PV 操作(信号灯)依赖于访问进程与共享变量在同一存储区。当进程分布在不同站点站点1、站点2共享内存不可用无法用 PV 实现互斥。
  • 管程方式:管程是被动的同步原语,但管程本身与调用进程在同一存储区,不适合分布环境

因此Ada 提出了会合机制:两个并发执行流汇集到一处——调用方发起调用,被调用方接受,握手、同步发生,且能传递参数。

2. 会合图示

  • 任务(被调用者)有多个 entry入口每个入口有一个 FIFO 队列保存等待的调用者。
  • 调用者发起 调用语句,被调用任务在 accept 处等待。
  • 双方在某入口汇合后,由被调用者代替调用者执行调用代码(即执行 accept 后面的语句序列)。
  • 会合结束后双方各自继续。

3. Ada 同步语句

1调用语句

<任务名称>.<入口名称>[<实参表>]

如:single_resource.acquire;

2接受语句

accept <入口名称>[<形参表>]
[do <语句序列> end <入口名称>]

accept 后面跟的 do...end 块即为会合期内被调用方执行的代码。

3选择语句 select

select
  [when <布尔表达式> =>]
      <接受语句>
      [<语句序列>]
  { or [when <布尔表达式> =>]
      <接受语句>
      [<语句序列>] }
  [ else <语句序列>]
end select

4. accept 语句的执行流程

有调用者?
   ├─ 等待 ─→ 选取第一个
   └─ 有 ─→ 会合开始
                ├─ 调用者等待
                ├─ 有 in 参数 ─→ 取 in 参数
                ├─ 有语句序列 ─→ 执行之
                ├─ 有 out 参数 ─→ 送 out 参数
                └─ 会合结束 ─→ 调用者继续

只有当调用者与被调用者双方都到达会合点时,会合才开始;任一方先到则等待对方。

5. select 语句语义(要点)

  1. 计算所有 when 后的布尔表达式,为真者对应入口标记为"开放"
  2. 若没有开放的 accept
    • 若有 else 部分,执行 else否则抛异常程序退出 select
  3. 若没有"开放且被调用的" accept
    • 若有 else,执行 else否则等待直到有调用者出现。
  4. 任取一开放且被调用的 accept,进入会合期,执行参数传递与会合代码。
  5. 会合结束后,若 select 后有语句序列,则继续执行。

6. 典型例子——单一资源管理

task single_resource is
  entry acquire;
  entry return;
end single_resource;

task body single_resource is
begin
  loop
    accept acquire;
    accept return;
  end loop
end single_resource;

-- 使用方
single_resource.acquire;
-- 使用资源
single_resource.return;

说明:因为 accept 之间是顺序的,所以 acquire 与 return 必须交替执行,天然保证了互斥。

7. 客栈问题Ada Lodge Problem——会合综合应用

场景:猎人(hunter)送来一份猎物,面包师(bakery)送来一份面包,主人(master)把面包+猎物做成三明治,探险家(explorer)进店取走一份三明治吃。一份猎物 + 一份面包 = 一份三明治

关键设计:

  • master 任务有三个 entrydeliverbreaddelivermeatlodge
  • select 中:
    • when bread=0 接受面包;
    • when meat=0 接受猎物;
    • when (sandwich<>0) or (bread<>0 and meat<>0) 接受探险家入店请求;
    • else 分支:当"有面包且有猎物且没三明治"时调用 makesandwich(栈主人主动做三明治)。

要点:会合期外仍可执行语句(如 sandwich:=0makesandwichselect 的 else 分支就属于"会合期外"。

8. 有界缓冲问题Bounded Buffer

task boundedbuffer is
  entry putin;
  entry getout;
end boundedbuffer;

task body boundedbuffer is
  b: array(0..k-1) of integer;
  ip, op: 0..k-1;
  count: integer;
begin
  ip:=0; op:=0; count:=0;
  loop
    select
      when (count<k) =>
        accept putin(item:in integer) do
          b(ip):=item
        end putin;
        ip:=(ip+1) mod k;
        count:=count+1;
    or when (count>0) =>
        accept getout(item:out integer) do
          item:=b(op)
        end getout;
        op:=(op+1) mod k;
        count:=count-1;
    end select
  end loop
end boundedbuffer;

通过 when 守卫保证缓冲区满时拒绝 putin、空时拒绝 getout。

9. 读者-写者问题(写者优先,作业#4

task readers_writers is
  entry start_read, finish_read;
  entry start_write, finish_write;
end readers_writers;

task body readers_writers is
  read_count, write_count: integer;
begin
  read_count:=0; write_count:=0;
  loop
    select
      when write_count=0 =>
        accept start_read do
          read_count:=read_count+1;
        end start_read
    or
      when read_count>0 =>
        accept finish_read do
          read_count:=read_count-1;
        end finish_read
    or
      when write_count=0 =>
        accept start_write do
          while read_count>0 do
            accept finish_read do
              read_count:=read_count-1;
            end finish_read
          end while
        end start_write;
        write_count:=write_count+1
    or
      when write_count>0 =>
        accept finish_write do
          write_count:=write_count-1;
        end finish_write
    end select
  end loop
end readers_writers;

写者优先的关键:在 accept start_write嵌套 accept finish_read,把当前所有读者"消化"完后再把 write_count 加1从而阻止后续新读者进入。


4.4 进程高级通讯

1. 进程通讯的层次

层次 通讯内容 机制
低级通讯 简单信号(少量控制信息) 进程互斥、进程同步(如 PV
高级通讯 大宗信息(数据/消息) 共享内存、消息传递

2. 高级通讯的分类(重要考点)

维度 类别 1 类别 2
通讯机制 共享内存memory sharing 消息传递message passing
寻址方式 直接(进程-进程) 间接(进程-信箱-进程)
命名对称性 对称sender 和 receiver 互相指名) 非对称(仅 sender 指名 receiver
缓冲方式 有缓冲 无缓冲

4.4.2 进程通讯模式

1. 共享内存模式Shared Memory

  • OS 提供一段公共内存区 M
  • 进程 P1、P2 通过 P/V 等互斥同步机制保证对 M 的安全访问。
  • 适用于单机多进程,效率高;但不能跨越机器

2. 消息传递模式Message Passing

  • 进程间通过 send / receive 原语交换消息。
  • 可分布式、跨网络;是分布式 OS 的主要通讯手段。

4.4.3 直接方式

1. 对称形式Symmetric

send(R, message)
receive(S, message)

发送方和接收方互相指名对方。优点是清晰;缺点是进程名变化时双方都要改。

2. 非对称形式Asymmetric仅 sender 指名)

send(R, message)
receive(pid, message)

只有发送者指名接收者,接收者由 pid(任一发送者)反向得到。常见于 C/S 模型:多个 clientsender向 serverreceiver发请求。


4.4.3.1 有缓冲途径(直接方式、非对称、有缓冲)

1. 消息结构

size | text | sender | link

2. 进程消息队列管理(每接收者一个队列)

Var Sm: semaphore;  -- 初值 0队列中消息计数
Var m_mutex: semaphore; -- 初值 1队列互斥
  • 接收方 receive 前:P(Sm)
  • 发送方 send 后:V(Sm)
  • 入列/出列动作夹在 P/V(m_mutex) 之间。

3. 缓冲池管理

Var Sb: semaphore;        -- 初值 k空缓冲数量
Var b_mutex: semaphore;   -- 初值 1缓冲池互斥
Head缓冲链头

申请空缓冲:P(Sb); P(b_mutex); 头缓冲出链; V(b_mutex); 释放空缓冲:P(b_mutex); 缓冲入链头; V(b_mutex); V(Sb);

4. send / receive 原语伪码

Send(R, M):
  根据R找接收者
  P(Sb); P(b_mutex); 取一空buf; V(b_mutex);
  buf <- (size, text, sender)
  P(m_mutex); 消息入链尾; V(m_mutex);
  V(Sm);

Receive(pid, N):
  P(Sm); P(m_mutex); 头消息出链; V(m_mutex);
  (size, text) -> N; sender -> pid
  P(b_mutex); 空buf入链; V(b_mutex);
  V(Sb);

重要说明send/receive 本身是高级通讯原语,但它们可以用低级原语PV来实现;而且 send/receive 并不是"真正不可中断"的原语OS 在执行它们的过程中仍可能被中断。


4.4.3.2 无缓冲途径(直接方式、非对称、无缓冲)

1. 实现机制

  • 收发双方 PCB 各有两个信号灯 S_mS_w,初值都为 0。
  • 不设缓冲,消息由发送者进程空间直接复制到接收者进程空间。
  • 必须收发双方同时到达会合点(会合性/rendezvous 语义)。

2. send 过程

Send(R, M):
  根据R找到消息接收者
  发送消息进程计数+1
  若接收进程在等待则将其唤醒 → V(S_m)
  等待消息传送完毕 → P(S_w)

3. receive 过程

Receive(pid, N):
  等待消息到达 → P(S_m)
  消息由发送进程空间复制到接收进程空间
  唤醒发送消息进程 → V(S_w)

4. 优缺点对比

优点 缺点
节省空间(不需要 buffer 并发性差:发送方必须等接收方 receive 完成复制后才能继续

4.4.4 间接方式Mailbox / 信箱)

1. 概念

进程间通讯通过一个共享信箱 mb 进行:

Send_mb(mb, m)
Receive_mb(mb, n)

拓扑灵活:

  • 多对一(多 sender — 一 receiver如服务器场景
  • 多对多(多 sender — 多 receiver

2. 信箱数据结构(属于 OS 空间)

type mailbox = record
  in, out: 0..k;          -- 环形队列指针
  s1, s2:  semaphore;     -- s1 初值 k空位数s2 初值 0消息数
  mutex:  semaphore;      -- 初值 1
  letter: array[0..k-1] of message;
end;
var mb: mailbox;

系统调用 create_mb(mb)delete_mb(mb) 创建/销毁信箱。

3. send_mb / receive_mb 实现

Procedure send_mb(var mb: mailbox; m: message):
  P(s1);                  -- 申请空位
  P(mutex);
  letter[in] := m;
  in := (in+1) mod k;
  V(mutex);
  V(s2);                  -- 消息数 +1

Procedure receive_mb(var mb: mailbox; var n: message):
  P(s2);                  -- 申请消息
  P(mutex);
  n := letter[out];
  out := (out+1) mod k;
  V(mutex);
  V(s1);                  -- 空位数 +1

关键点

  • s1s2 一对信号灯实现了生产者-消费者约束(空位 vs 消息)。
  • mutex 实现对信箱内部数据结构的互斥访问。
  • 信箱属于 OS 空间,由 OS 统一管理生命周期。

4. 信箱空间归属小结

归属 说明 生命周期
OS 空间(如上) 由 OS 创建/销毁 进程结束信箱仍可保留
进程空间 由某进程创建,进程退出即失效 与进程同生共死

UNIX 进程高级通讯机制

1. 管道Pipe

  • 基于文件系统实现与普通文件使用统一的界面open/read/write/close
  • 两次 IO:读、写各一次。
  • 速度优化
    • 延迟写delayed write:先写到内存缓冲区,内存资源不紧张且缓冲区不被占用时不会刷到外存。
    • 读时优先从内存缓冲区得到数据。
  • 大小限制:通常 ~2KB超出后循环使用避免缓冲资源紧张。
  • 适用:父子/兄弟进程等有亲缘关系的进程(无名管道)。

2. UNIX 信号Signal

  • 每个进程 PCB 中有 p_sig 字段记录接收到的信号。
  • user 结构中有 u_signal[NSIG] 数组保存信号处理程序入口。
  • 系统调用 signal(sig, func) 预置处理程序:
    • func = 0:收到信号时进程终止自己
    • func = 奇数忽略该信号。
    • func = 偶数:作为处理程序入口地址
  • 子进程继承父进程的信号处理程序。
  • 信号任何时候都可接收,仅记录在 p_sig 中。
  • 信号处理时机:进程被调度选中、即将由**核心态转为目态(用户态)**时。
  • 信号对用户而言是一种"软中断"。

Windows 10 的管道

类型 命名 标识 关键 API
无名管道Anonymous 句柄 hReadPipehWritePipe CreatePipe
命名管道Named \\.\pipe\PipeName(服务端)/ \\serverName\pipe\PipeName(客户端) 文件系统路径 CreateNamedPipeConnectNamedPipeCallNamedPipeReadFile/WriteFile
  • CreateNamedPipe:服务器端建立管道。
  • ConnectNamedPipe:服务器端等待客户连接请求。
  • CallNamedPipe:客户端建立到服务器管道的连接并通讯。
  • ReadFile / WriteFile:管道读写(与文件 I/O 同一套接口)。

Windows 10 的 Mailslot邮件槽

  • 服务端路径\\.\mailslot\[path]name(本地)
  • 客户端路径\\range\mailslot\[path]namerange 是域名,表示发往哪台主机)
  • 关键 API
    • CreateMailslot:服务端创建邮件槽。
    • GetMailslotInfo:服务端查询邮件槽状态(消息数、最大长度等)。
    • ReadFile:服务端读取消息。
    • WriteFile:客户端发送消息。

特点:基于广播/域内投递,适合一对多的轻量级消息通讯。


概念对比小结(考试最爱)

机制 拓扑 缓冲 同步性 适合场景
共享内存 1 对 1 或多对多 用户自管 用户自管 单机高速数据交换
直接消息 有缓冲 1 对 1 或多对 1 OS 维护消息队列 异步(发送后即可返回) C/S、Producer-Consumer
直接消息 无缓冲 1 对 1 会合(同步) 强同步握手
间接消息(信箱) 多对 1 / 多对多 OS 信箱 异步 多发送者场景、解耦
管道 通常 1 对 1亲缘 内核缓冲 字节流 shell 流水线
命名管道 C/S 内核缓冲 同步/异步 跨进程、跨网络Windows
邮件槽 mailslot 多对多(域) 内核 不可靠/单向 Windows 局域网广播

二、考点总结

考点1会合Rendezvous的概念与适用场景

  • 内容:会合是 Ada 语言提出的、用于分布式系统的同步通讯机制。两个并发执行流在"调用语句"与"accept 语句"处汇合,握手、同步发生,由被调用方代替调用方执行会合代码。引入背景是共享变量/管程无法在分布式站点间使用。
  • 考查方式:选择、填空、简答。
  • 可能的题目
    1. (填空)会合机制是为解决 __________ 环境下共享变量与管程不可用的问题而提出的。
      • 答案:分布式(多站点)
    2. 简答为什么共享变量方式PV不适合分布式系统
      • 答案PV 操作的共享变量必须与访问进程位于同一存储区;分布式系统中各进程分布在不同站点,没有共享内存,因此无法用 PV 实现互斥/同步。

考点2Ada 的三类同步语句

  • 内容:调用语句 <任务>.<入口>[<实参>];接受语句 accept <入口>[<形参>] [do <语句序列> end <入口>];选择语句 select ... or ... [else ...] end select
  • 考查方式:填空、简答、代码阅读。
  • 可能的题目
    1. 填空Ada 中被调用任务用以接受调用请求的语句是 __________。
      • 答案:accept 语句
    2. (代码)写出单一资源管理器的 Ada task 框架。
      • 答案:见上文"单一资源管理"示例,含 accept acquire; accept return; 顺序循环。

考点3accept 的会合期执行流程

  • 内容:调用者发起调用→任务在 accept 等待→双方到齐会合开始→若有 in 参数则取 in 参数→若有语句序列则执行→若有 out 参数则送 out 参数→会合结束,调用者继续。
  • 考查方式:选择、流程图题。
  • 可能的题目
    1. (选择)关于 accept 语句下列说法正确的是__
      • A) 仅调用者到达会合点即可开始会合
      • B) 仅被调用者到达会合点即可开始会合
      • C) 双方都到达会合点时才会合才开始(正确答案)
      • D) 会合由调用方执行会合代码

考点4select 语句的语义when / else / 等待)

  • 内容:先算所有 when 布尔式,为真者对应入口开放;若没有开放 accept 且无 else 则异常;若没有"开放且被调用的" accept 则等待(或执行 else任选一开放且被调用的 accept 执行会合。
  • 考查方式:选择、读程序、写程序。
  • 可能的题目
    1. 选择select 语句中若所有 when 条件都为假、且没有 else 部分则行为是__
      • 答案:抛异常(异常退出 select
    2. (选择)若所有开放入口都无调用者、且没有 else则行为是__
      • 答案:等待直到有调用者出现

考点5会合的典型应用——客栈问题

  • 内容master 任务的三个 entrydeliverbread、delivermeat、lodge通过 select 的 when 守卫实现"一份面包 + 一份猎物 = 一份三明治"的同步约束else 分支在会合期外主动 makesandwich
  • 考查方式:代码补全、简答。
  • 可能的题目
    1. (简答)客栈问题中 master 如何保证每次只做一份三明治?
      • 答案:通过 sandwichbreadmeat 三个状态变量 + select 的 when 守卫实现lodge 入口需 sandwich≠0 或 (bread≠0 且 meat≠0)else 分支在原料齐备时主动制作。做完/取走后立即清零。

考点6会合的典型应用——有界缓冲问题

  • 内容:用 count 与模 k 的环形缓冲,通过 select 的 when count<kwhen count>0 实现生产者-消费者约束。
  • 考查方式:选择、改写。
  • 可能的题目
    1. 选择Bounded buffer 的 putin 入口的守卫条件是 ________。
      • 答案:count < k(缓冲区未满)

考点7会合实现读者-写者问题(写者优先)

  • 内容:用 read_countwrite_count 计数;在 accept start_write嵌套 accept finish_read,把所有活跃读者消化完后再使 write_count 加 1从而阻止后续新读者。
  • 考查方式:代码阅读、简答。
  • 可能的题目
    1. (简答)为什么把 accept finish_read 嵌套在 accept start_write 内部即可实现"写者优先"
      • 答案:嵌套的 accept 在会合期内会持续接收并处理读者退出请求,直到 read_count=0,然后会合结束、写者才能进入并把 write_count 增 1。这样在已有写者等待时新读者被 when write_count=0 守卫阻挡,无法进入,从而保证写者优先。

考点8进程高级通讯的分类

  • 内容:高级通讯按四种维度分类:共享内存 vs 消息传递;直接 vs 间接;对称 vs 非对称;有缓冲 vs 无缓冲。
  • 考查方式:选择、填空、简答。
  • 可能的题目
    1. (填空)高级通讯的两大机制是 __________ 和 __________。
      • 答案:共享内存memory sharing消息传递message passing
    2. 选择C/S 模型中常见的是哪种命名方式?
      • A) 对称形式 B) 非对称形式(仅 sender 指名 receiver(正确答案)

考点9直接消息——有缓冲途径的 PV 实现

  • 内容:每接收者维护消息队列,需要三组信号灯:Sm消息计数初值0m_mutex队列互斥初值1Sb空缓冲数初值kb_mutex缓冲池互斥初值1。send 时申请空 buf、填入消息、入队、V(Sm)receive 时 P(Sm)、出队、归还 buf、V(Sb)
  • 考查方式代码补全、PV 计算、应用题。
  • 可能的题目
    1. (填空)有缓冲消息队列中,Sm 的初值是 ________m_mutex 的初值是 ________。
      • 答案:Sm=0m_mutex=1
    2. (应用)画出 send(R,M) 完整 PV 序列。
      • 答案:P(Sb); P(b_mutex); 取空buf; V(b_mutex); 填 buf; P(m_mutex); 入队; V(m_mutex); V(Sm);

考点10直接消息——无缓冲途径会合语义

  • 内容:收发双方各设 S_mS_w初值0。发送方 V(S_m)P(S_w) 等待;接收方 P(S_m) 后复制消息,再 V(S_w)消息直接从发送进程空间复制到接收进程空间,无需缓冲。
  • 考查方式PV 顺序题、简答。
  • 可能的题目
    1. (填空)无缓冲途径下,发送方发送消息后必须执行 __________ 才能继续。
      • 答案:P(S_w)(等待接收方复制完消息)
    2. (简答)无缓冲途径相对有缓冲途径的优缺点。
      • 答案:优点——节省空间(不需要缓冲);缺点——并发性差,发送方必须等接收方执行 receive 把消息复制到接收进程空间后才能继续。

考点11间接方式——信箱通讯

  • 内容:信箱属于 OS 空间,含环形队列 letter[in..out]、信号灯 s1(空位=ks2(消息=0mutex(互斥=1。send_mbP(s1); P(mutex); letter[in]:=m; in++; V(mutex); V(s2);。receive_mb反向。
  • 考查方式PV 顺序、代码补全。
  • 可能的题目
    1. (填空)信箱通讯中 s1 表示 ________、s2 表示 ________。
      • 答案:s1 表示空位数初值ks2 表示消息数初值0
    2. (选择)信箱机制最适合的场景是:
      • A) 1对1 同步握手 B) 多对一 或 多对多,解耦发送者和接收者(正确答案)

考点12UNIX 管道Pipe

  • 内容:基于文件系统、与文件统一界面;速度优化包括延迟写(写到内存缓冲,不立刻刷盘)和大小限制如2KB循环使用通常用于亲缘进程无名管道
  • 考查方式:选择、填空、简答。
  • 可能的题目
    1. 填空UNIX pipe 的速度优化措施有 ________ 和 ________。
      • 答案:延迟写delayed write缓冲大小限制(循环使用)
    2. (简答)为什么 pipe 虽然是"文件"但一般不会真的执行磁盘 I/O
      • 答案pipe 采用延迟写策略,数据先写入内存缓冲区,内存资源不紧张时不会刷盘;读时直接读内存缓冲,因此基本不发生磁盘 I/O。

考点13UNIX 信号Signal

  • 内容p_sig 字段记录收到的信号;u_signal[NSIG] 数组保存处理入口;signal(sig,func) 中 func=0 终止、奇数忽略、偶数为处理入口;子进程继承父进程信号表;信号处理时机是由核心态转目态前
  • 考查方式:选择、填空。
  • 可能的题目
    1. 填空UNIX 中,进程被调度选中即将由核心态转为目态时,会检查并处理 PCB 中的 ________ 字段。
      • 答案:p_sig(信号字段)
    2. (选择)signal(sig, func)func 为奇数表示__
      • A) 终止进程 B) 忽略该信号(正确答案) C) 作为处理入口 D) 出错

考点14Windows 命名管道 vs 无名管道

  • 内容:无名管道通过 CreatePipe 创建,仅本机有亲缘关系的进程可用;命名管道使用路径名 \\.\pipe\PipeName\\serverName\pipe\PipeName,可跨网络,支持 C/S关键 API 有 CreateNamedPipeConnectNamedPipeCallNamedPipeReadFile/WriteFile
  • 考查方式:选择、填空、应用。
  • 可能的题目
    1. 填空Windows 中,命名管道服务端路径格式为 ________客户端为 ________。
      • 答案:\\.\pipe\PipeName\\serverName\pipe\PipeName
    2. (选择)下列哪个 API 是客户端用来连接命名管道的?
      • 答案:CallNamedPipe

考点15Windows Mailslot邮件槽

  • 内容:服务端 \\.\mailslot\[path]name,客户端 \\range\mailslot\[path]name支持一对多的轻量级不可靠消息通讯APICreateMailslotGetMailslotInfoReadFileWriteFile
  • 考查方式:选择、填空。
  • 可能的题目
    1. (填空)邮件槽主要适用于 ________ 的消息通讯场景。
      • 答案:一对多(域内广播)/ 轻量级消息
    2. 选择服务端查询邮件槽状态应调用__
      • 答案:GetMailslotInfo

考点16综合——各通讯机制对比

  • 内容:共享内存 vs 消息传递、直接 vs 间接、有缓冲 vs 无缓冲、管道 vs 命名管道 vs mailslot 在拓扑、同步性、缓冲、典型场景上的差异。
  • 考查方式:表格题、简答。
  • 可能的题目
    1. (简答)比较"直接消息 无缓冲"与"直接消息 有缓冲"在同步性、空间开销、并发性上的差异。
      • 答案:① 同步性无缓冲是会合式同步必须同时到达有缓冲是异步send 后即可返回)。② 空间:无缓冲不需要缓冲,节省空间;有缓冲需要缓冲池(开销=k 个缓冲 + 队列)。③ 并发性:无缓冲并发性差(发送方必须等待接收方 receive 复制完成);有缓冲并发性好(消息进入队列即返回)。
    2. (简答)比较消息传递与共享内存的优缺点。
      • 答案:消息传递——易于分布式、跨网络,编程简单,但有数据拷贝开销;共享内存——效率高(无拷贝)、适合大数据量,但必须自行解决互斥同步,且不适合分布式