想象你是一家繁忙餐厅的后厨总协调员。晚市高峰,五个厨师同时开工:有人切菜、有人掌勺、有人负责烤箱、有人传菜。他们共用同一张备料台、同一台烤箱、同一条出菜通道。如果没有任何规矩,两个厨师会同时抢一把刀,烤箱被一个慢吞吞的厨师占着导致最急的菜出不来,传菜口菜品堆成山却没人拿。于是你定了几条规矩:备料台前的“取料票”、烤箱旁的“唯一钥匙”、传菜口上的“传送带”、每件事的“最晚等待时间”。这间厨房,就是一台实时操作系统(RTOS);这些规矩,就是任务间同步与通信的三大武器——信号量、互斥锁、消息队列。本文用这间厨房的烟火气,带你彻底看懂嵌入式孪生世界里,多任务如何有序协作、实时通信。

岗位关联

对 AI 从业者而言,理解 RTOS 同步机制是打通“算法部署”与“硬件实时响应”的最后一公里——无论跑视觉识别还是语音唤醒,多线程协作的稳定性都取决于这几个机制是否用得漂亮。

目录
  1. 可视化演示
  2. 同步与共享资源
  3. 信号量机制
  4. 互斥锁与优先级反转
  5. 消息队列机制
  6. 阻塞与超时策略
  7. 生产者消费者模型
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示信号量P/V操作与等待队列、互斥锁优先级继承、消息队列生产者-消费者模型及死锁预防策略。

本节配有交互动效(进入视口后自动加载,约 3KB)。

信号量P/V操作与等待队列 P操作减计数为0则阻塞入队 V操作加计数唤醒等待任务 P: if(cnt>0) cnt--; else block() 信号量基于计数器与等待队列实现同步

同步与共享资源

晚市厨房里,最怕的不是菜多,而是两个厨师同时把手伸进同一只调料罐。A 刚把盐舀走一半,B 又补了一勺,最后这道菜的咸淡完全乱套。多任务系统也一样:两个任务同时读写同一个全局变量、同一块外设寄存器,结果数据在“半新不旧”的状态下被另一方读走,这叫竞态(Race Condition)。竞态的根源,是多个执行流“同时”触碰了同一份资源,而它们本应互相错开。

这间厨房需要一条规矩:谁进了备料区,别人就得在外面等。这段只允许一个任务进入的代码区域,叫临界区(Critical Section)。临界区的访问必须满足互斥——你有我无、你进我退。RTOS 提供的信号量、互斥锁、消息队列,本质都是“让临界区有序”的工具。它们被统称为同步原语,就像后厨里的那本“通行规矩手册”。

临界区的数学模型很直白,任务 A 和任务 B 交替进入:

回到厨房:正是这些“取钥匙—干活—还钥匙”的规矩,才让五双手在同一个备料台上安稳地轮流作业。没有规矩,再快的厨师也架不住一场混战;有了规矩,多任务的并发才真正变得安全有序。

enter:P(mutex)→临界区操作→V(mutex)\text{enter:} \quad P(\text{mutex}) \quad \to \quad \text{临界区操作} \quad \to \quad V(\text{mutex})
通俗解释:每个任务进门前先“取钥匙”(P),操作完“还钥匙”(V)。谁拿到钥匙谁进,没拿到的在门口排队。
  1. P(mutex)互斥锁的“取锁”操作,尝试把锁从“空闲”变为“占用”。
    P(mutex):若锁空闲则取走,否则排队等待P(\text{mutex}): \text{若锁空闲则取走,否则排队等待}
  2. 临界区操作真正读写共享资源的代码段,这段区域严密保护。
    Critical Section:对共享数据的原子操作\text{Critical Section}: \text{对共享数据的原子操作}
  3. V(mutex)互斥锁的“释放”操作,把锁归还,唤醒一个等待者。
    V(mutex):锁释放,等待队列队首任务被唤醒V(\text{mutex}): \text{锁释放,等待队列队首任务被唤醒}

信号量机制

备料台前挂着一串木牌,每块木牌代表“领一份食材的资格”。厨师想拿菜,先取一块木牌;用完把木牌挂回去。这串木牌就是信号量。信号量是一个非负整数计数器,配上两个操作:P 操作(Proberen,荷兰语“测试”)把计数减一,V 操作(Verhogen,荷兰语“增加”)把计数加一。

如果这串木牌只有一块,就叫二值信号量——像电影院门口的单人通行证,适合表示“有没有”。它可以承担互斥职责:任务进入临界区前 P,出来后 V,保证同一时刻只有一个人“持证上岗”。如果有 N 块木牌,就叫计数信号量——像停车场里 N 个空位的计数牌,管理一批同类资源,例如内存池里的 5 个缓冲区、串口 DMA 的 4 个描述符。

P/V 操作是信号量的灵魂。共享资源总量为 init,已被占用的资源数为 used,当前剩余计数 s 的关系是:

后厨最后挂上一块告示牌:“木牌取尽,请排队”。信号量 max 让资源占用变得透明:谁也别想多拿,谁也别想饿死谁。它管的是“数量”——几把刀、几个灶眼、几块缓冲区,清清楚楚。

s=init−used(0≤s≤init)s = init - used \quad (0 \le s \le init)
通俗说明:木牌总数固定,挂在外面的越少,说明被领走的越多;当 s=0 时,再来的任务只能排队。
  1. init(初始值)信号量创建时传入的令牌总数,对应资源总量。
    init=资源总数(木牌数)\text{init} = \text{资源总数(木牌数)}
  2. used(已用数)当前被任务持有的令牌数量,每 P 一次就加一。
    used=已执行 P 的次数−已执行 V 的次数\text{used} = \text{已执行 P 的次数} - \text{已执行 V 的次数}
  3. s(当前计数)剩余可用令牌数,永远在 0 到 init 之间。
    s=init−used,s=0 时任务阻塞s = \text{init} - \text{used},\text{s}=0 \text{ 时任务阻塞}

互斥锁与优先级反转

烤箱钥匙只有一把,谁拿了谁用,用完必须交回。互斥锁(Mutex)就是这把钥匙,但它比二值信号量更“较真”:必须由同一个任务获取并释放。A 任务上了锁,绝不允许 B 任务来解锁——防止你把钥匙弄丢在别人手里。同时互斥锁还支持递归获取:同一个任务可以多次上锁,嵌套几次就得解几次。

厨房里的经典灾难来了:烤箱被最空闲的帮厨占着烤面包,而最急的主厨等着烤箱做招牌菜,偏偏帮厨优先级极低,被其他杂事打断半天没回来开烤箱。于是高优先级任务反而等最低优先级任务,这叫优先级反转(Priority Inversion)。

解法是优先级继承:当高优先级任务被低优先级任务阻塞时,系统临时把低优先级任务的优先级抬升到高任务同一档,让它赶紧把锁释放。用公式表达,设高优任务优先级为 $\text{Pr}_H$,低优任务当前有效优先级为 $\text{Pr}_L^{\text{eff}}$:

这把“会临时升职”的钥匙,完美化解了高优任务被低优任务卡死的尴尬。用互斥锁而不是二值信号量来保护临界区,正是看中了它的归属感、递归能力和优先级继承,这些都是普通信号量给不了的“高级钥匙功能”。

PrLeff=max⁡(PrL, PrH)当低优任务持有高优任务所需的锁时\text{Pr}_L^{\text{eff}} = \max(\text{Pr}_L,\ \text{Pr}_H) \quad \text{当低优任务持有高优任务所需的锁时}
通俗说明:低优厨师临时“升职”成主厨级别,优先把手里的活干完交钥匙,高优任务才不用干等。
  1. Pr_L(低优任务原始优先级)任务创建时给定的基础优先级,本应很低。
    PrL=低优先级任务的基础优先级\text{Pr}_L = \text{低优先级任务的基础优先级}
  2. Pr_H(高优任务优先级)等待该锁的最高优先级任务的优先级。
    PrH=阻塞方中的最高优先级\text{Pr}_H = \text{阻塞方中的最高优先级}
  3. max 取大有效优先级取两者最大值,实现临时提升。
    PrLeff=max⁡(PrL, PrH)\text{Pr}_L^{\text{eff}} = \max(\text{Pr}_L,\ \text{Pr}_H)
  4. 锁释放后恢复V 操作后低优任务优先级还原。
    PrLeff→PrL(释放锁后)\text{Pr}_L^{\text{eff}} \to \text{Pr}_L \quad (\text{释放锁后})

消息队列机制

菜做好了不能直接塞给传菜员手里——那样两个人得时刻保持同步。后厨的做法是:把菜放到一条传送带上,传菜员从另一端按顺序取走。消息队列就是这个传送带,它是 RTOS 内核里一个先进先出(FIFO)的数据管道:发送任务把消息“写入”队尾,接收任务从队头“读出”消息。

队列机制与信号量的区别在于:信号量只传递“数量”,消息队列传递“内容”。一个传感器任务把采集到的温度值、一个按键任务把按键码,都打包成一条条消息,放进队列。接收任务无需关心发送者是谁、何时发送,只要从队头取数据即可。队列满时发送方阻塞,队空时接收方阻塞——阻塞可以带超时,也可以无限等待。

消息从发送方到接收方,数据经历了“拷贝进去再拷贝出来”的过程:

传送带让发送方和接收方彻底解耦:发送方不用等接收方“伸手来接”,接收方也不用时刻盯着发送方。队列不仅缓冲了速率不匹配,还天然隔离了数据——每条消息都有独立的存储空间,不会像共享变量那样互相踩踏。这条传送带,是嵌入式系统里最常用的“任务间对话管道”。

cost=memcpy(queue, msg)⏟入队拷贝+memcpy(msg, queue)⏟出队拷贝\text{cost} = \underbrace{\text{memcpy}(\text{queue},\ \text{msg})}_{\text{入队拷贝}} + \underbrace{\text{memcpy}(\text{msg},\ \text{queue})}_{\text{出队拷贝}}
通俗说明:数据像菜品一样,先端上传送带,再从传送带端走,两趟搬运就是两次内存拷贝。
  1. 入队拷贝发送方把消息数据复制到内核队列缓冲区,涉及一次内存拷贝。
    queue[tail]←msg, tail=(tail+1) mod len\text{queue}[tail] \gets \text{msg},\ \text{tail} = (\text{tail}+1)\ \text{mod}\ \text{len}
  2. 出队拷贝接收方从队列头把数据复制到自己的缓冲区。
    msg←queue[head], head=(head+1) mod len\text{msg} \gets \text{queue}[head] ,\ \text{head} = (\text{head}+1)\ \text{mod}\ \text{len}
  3. 总开销两次拷贝的耗时之和,是消息队列无法回避的代价。
    cost=copyin+copyout≈2×msg_size\text{cost} = \text{copy}_{in} + \text{copy}_{out} \approx 2 \times \text{msg\_size}

阻塞与超时策略

客人催菜,后厨不能永远干等。厨师在传送带前等一道新菜,叫阻塞(Blocking);如果等了 30 秒还没等到,转身去干别的,等会儿再来看——这叫超时(Timeout)。RTOS 的阻塞等待毫秒级精确:如果队列空,接收任务进入阻塞态;若指定了超时时间 timeout,最晚等到那一刻,再等不到就返回“超时错误”。

阻塞与超时是所有同步原语的共同参数:信号量取不到令牌可以带超时,互斥锁被占用也可以带超时,消息队列读写更是标配超时。带超时的等待让系统有了“反悔”的能力,避免任务无限期挂在等待队列里,导致整个系统“假死”。可以这样建模:

这个“赛跑模型”是嵌入式工程师规避死锁的底气。设置超时,就像给每一条等待都买了份“止损保险”:等不到值,至少等得到结果。消息队列、信号量、互斥锁,凡是能“等”的操作,几乎都支持这个保险机制。它把“永远死等”变成了“有界等待”,让系统的每个卡点都有出口。

status={OK,若在 twait≤timeout 内获得资源TIMEOUT,若 twait>timeout\text{status} =\begin{cases}\text{OK}, & \text{若在 } t_{wait} \le \text{timeout} \text{ 内获得资源} \\\text{TIMEOUT}, & \text{若 } t_{wait} > \text{timeout}\end{cases}
通俗说明:等待时间和超时时间赛跑。赛跑赢了返回成功,输了返回超时错误码,任务该干嘛干嘛去。
  1. t_wait(实际等待时长)从任务发起等待到获得资源经过的时间。
    twait=获得资源时刻−发起等待时刻t_{wait} = \text{获得资源时刻} - \text{发起等待时刻}
  2. timeout(超时上限)任务愿意等待的最长时间,由调用者传入。
    timeout=任务可承受的最大阻塞时长\text{timeout} = \text{任务可承受的最大阻塞时长}
  3. 分支判断t_wait 不超限则成功,超限则放弃。
    status={OK,twait≤timeoutTIMEOUT,twait>timeout\text{status} = \begin{cases} \text{OK}, & t_{wait} \le \text{timeout} \\ \text{TIMEOUT}, & t_{wait} > \text{timeout} \end{cases}

生产者消费者模型

把前面所有工具组装成最经典的套路:一间厨房里,切菜师傅(生产者)不停把半成品放到备料台上,炒菜师傅(消费者)不停从备料台取走半成品。备料台只有 5 个位置,满了不能放,空了不能取。用三个信号量就能完美编排这场协作:empty 记录空位数,full 记录已有菜数,mutex 保护备料台的互斥访问。

生产者先 P(empty) 确认有空位,再 P(mutex) 进备料区,放好后 V(mutex) 出来,最后 V(full) 通知“多了一道菜”。消费者顺序恰好相反:先 P(full) 确认有菜可取,再 P(mutex) 进区取走,V(mutex) 出区,最后 V(empty) 通知“多了一个空位”。核心流程:

这就是嵌入式世界里大名鼎鼎的“生产者-消费者”模型,它几乎出现在所有业务逻辑中:串口接收中断往队列里塞数据,主循环从队列里取数据解析;音频采集任务往缓冲区放入数据块,算法任务取出做推理。生产者只关心“有没有空位”,消费者只关心“有没有数据”,两者通过信号量默契配合,互不干扰。这间后厨的烟火气,最终化作一行行稳定的代码,在每秒处理千万指令的芯片上稳稳运转。

Producer: P(empty)→P(mutex)→放入→V(mutex)→V(full)\text{Producer: } P(\text{empty}) \to P(\text{mutex}) \to \text{放入} \to V(\text{mutex}) \to V(\text{full})
Consumer: P(full)→P(mutex)→取出→V(mutex)→V(empty)\text{Consumer: } P(\text{full}) \to P(\text{mutex}) \to \text{取出} \to V(\text{mutex}) \to V(\text{empty})
通俗说明:生产者和消费者通过“空位牌”和“有菜牌”互相通报状态,而互斥锁保证同一时刻只有一个人伸手进备料台。
  1. 生产者等空位P(empty) 确保缓冲区有空位置才继续,否则阻塞。
    P(empty):empty←empty−1, empty≥0P(\text{empty}): \text{empty} \gets \text{empty} - 1,\ \text{empty} \ge 0
  2. 生产者取互斥锁P(mutex) 保证放菜过程不被消费者打断。
    P(mutex):进入临界区,互斥访问缓冲区P(\text{mutex}): \text{进入临界区,互斥访问缓冲区}
  3. 生产者放菜并通知放好后释放锁并 V(full) 增加“有菜牌”。
    V(mutex):出临界区;V(full):full←full+1V(\text{mutex}): \text{出临界区};\quad V(\text{full}): \text{full} \gets \text{full} + 1
  4. 消费者对称操作消费者 P(full) 等有菜、P(mutex) 进区、出区 V(mutex)、V(empty)。
    P(full)→P(mutex)→取出→V(mutex)→V(empty)P(\text{full}) \to P(\text{mutex}) \to \text{取出} \to V(\text{mutex}) \to V(\text{empty})

AI 应用场景

语音唤醒流水线

阶段:数据采集与预处理

解决的问题:传统嵌入式语音方案中,录音任务和算法任务直接共享音频缓冲,经常出现“录到一半被读走”的错乱数据,导致唤醒率骤降。用消息队列构建音频流水线后,录音任务把音频帧写入队列,唤醒模型任务从队列取帧,两者速率不匹配时有队列缓冲兜底。

具体案例:一款智能音箱的唤醒词检测,麦克风中断以 16kHz 持续采集音频,中断服务程序将每 10ms 的音频帧打包成消息发送到队列;运行神经网络唤醒模型的任务阻塞在队列读取上,拿到帧即推理。队列深度设为 30 帧,正好缓冲 300ms 音频,保证任何调度延迟下都不丢帧。

机器人传感器融合

阶段:多模态特征对齐与融合

解决的问题:机器人同时运行 IMU、激光雷达、摄像头多个传感器任务,每个任务频率不同、数据到达时间不固定。如果直接共享全局变量,融合算法读到的是“新旧混杂”的数据。使用同步信号量加消息队列,每个传感器任务独立采集并发送带时间戳的消息,融合任务按队列顺序处理。

具体案例:一款配送机器人的感知模块,IMU 任务以 200Hz 发送姿态消息,视觉任务以 30Hz 发送目标检测消息,融合任务在 RTOS 的三个消息队列上有选择地接收,并以最新时间戳对齐。信号量保证底层外设寄存器不被两个任务同时读写。

边缘 NPU 推理调度

阶段:模型推理与后处理

解决的问题:边缘设备上 AI 推理任务和通信任务共用计算资源,推理任务占用 NPU 时,通信任务若得不到调度就会丢包。用互斥锁保护 NPU 驱动句柄,用信号量协调推理与传输的节奏,保证高优先级通信任务不被推理任务长时间卡死。

具体案例:某安防摄像头搭载 RTOS 与轻量级神经网络,检测任务负责跑模型,图传任务负责把结果发送给网关。互斥锁保护共享的编码器硬件,检测任务持锁执行 NPU 推理,图传任务等待锁时启用优先级继承,避免高优图传被低优推理长期阻塞。

Python 演示

python66 行
import threadingimport queueimport randomimport time SHARED_BUFFER = {}BUFFER_LOCK = threading.Lock()ORDER_QUEUE = queue.Queue(maxsize=3) class Producer(threading.Thread):    def __init__(self, name, orders, store):        super().__init__(name=name)        self.orders = orders        self.store = store        self.p_count = 0     def run(self):        for dish in self.orders:            ORDER_QUEUE.put(dish)            with BUFFER_LOCK:                self.store['made'] = self.store.get('made', 0) + 1                self.p_count += 1                print(self.name, 'produce', dish)            time.sleep(0.05)        print(self.name, 'done, count=', self.p_count)  class Consumer(threading.Thread):    def __init__(self, store):        super().__init__(name='Consumer')        self.store = store        self.c_count = 0     def run(self):        while self.c_count < 6:            try:                dish = ORDER_QUEUE.get(timeout=0.2)            except queue.Empty:                print('Consumer timeout, retry')                continue            with BUFFER_LOCK:                self.store['eaten'] = self.store.get('eaten', 0) + 1                self.c_count += 1                print(self.name, 'consume', dish)            time.sleep(0.1)        print('Consumer done, count=', self.c_count)  def main():    store = {}    workers = [        Producer('ChefA', ['soup', 'rice', 'beef'], store),        Producer('ChefB', ['fish', 'tofu', 'egg'], store),    ]    customer = Consumer(store)    for w in workers:        w.start()    customer.start()    for w in workers:        w.join()    customer.join()    print('store=', store)  if __name__ == '__main__':    main()
目的:用 Python 标准库模拟 RTOS 中信号量/互斥锁/消息队列的协作,两个生产者线程向队列放消息,一个消费者线程取消息,共享计数器用锁保护。
  1. ORDER_QUEUE = queue.Queue(maxsize=3)消息队列最多缓存 3 条消息,对应 RTOS 队列深度,满了生产者自然阻塞。
  2. ORDER_QUEUE.put(dish)生产者入队操作,队列满时自动阻塞,模拟 RTOS 的“带阻塞入队”。
  3. ORDER_QUEUE.get(timeout=0.2)消费者出队并设置超时,0.2 秒取不到就返回 queue.Empty 异常,对应 RTOS 超时返回错误码。
  4. with BUFFER_LOCK:互斥锁保护共享字典 store,防止两个线程同时写计数导致数据错乱。
  5. threading.Thread` 与 `start/join对应 RTOS 任务创建、就绪与同步结束。

常见误区

误区“二值信号量和互斥锁都是 0/1,反正效果一样,我直接用信号量保护临界区就行。”

正解二值信号量没有“持有者”概念,任务 A 可以执行 V 释放任务 B 获取的信号量,这把钥匙随时可能被别人偷走;而互斥锁强制“谁上锁谁解锁”,还支持递归获取与优先级继承。在保护共享资源、尤其是高优先级任务参与的场景,必须用互斥锁。

为何常见:初学者只看到二者取值为 0/1 的表面相似性,忽略了互斥锁归属关系和优先级继承这两个隐藏能力,以为只是名字不同。

误区“优先级继承就是永久提高任务优先级,低优任务从此一直享受高优待遇。”

正解优先级继承是临时机制,只在低优任务持有高优任务需要的关键资源时生效;一旦锁释放,低优任务的优先级立即恢复原值。永久提权会破坏系统的实时调度公平性,导致其他任务饿死。

为何常见:把“有效优先级”与“基础优先级”混为一谈,忽略了继承是条件触发且动态撤销的,这是视频课程里最常被一句带过的细节。

误区“反正资源越宽松越好,信号量初始值给大一点,任务就不容易等了。”

正解信号量初始值必须等于实际可共享的同类资源数。给大了虽然任务不阻塞,但多个任务可能同时操作超出实际能力的资源,破坏资源边界;给小了则造成不必要的阻塞,降低并发度。

为何常见:凭直觉“给大更安全”,实际上信号量本质是资源契约,数值失真等价于承诺了不存在的资源,问题会在极端负载下爆发。

误区“队列满了,后来的消息就丢了,反正嵌入式系统经常这样。”

正解RTOS 消息队列在满员时默认让发送任务阻塞,直到队空出位置,或超时后返回错误码,发送者仍持有数据,可自行决定重发或丢弃。丢弃行为只在明确调用“非阻塞发送”或使用覆盖式队列时才会发生,绝不是默认解。

为何常见:很多教程演示队列时忽略了满员分支,初学者只看到“入队成功”的画面,便以为队列有丢包属性。

面试题

Q1. 在 RTOS 中,保护共享全局变量最合适的同步原语是?

单选题 · easy

  • A.二值信号量
  • B.互斥锁← 正确答案
  • C.计数信号量
  • D.消息队列

答案:B。互斥锁具备持有者归属、递归获取和优先级继承能力,专为临界区保护设计。二值信号量虽能互斥,但没有归属关系也不支持优先级继承;计数信号量管数量不管互斥;消息队列用于传数据而非直接保护变量。

Q2. 经典优先级反转问题中,谁在“霸占”共享资源导致高优任务等待?

单选题 · mid

  • A.高优先级任务本身
  • B.中优先级任务
  • C.正在操作共享数据的低优先级任务← 正确答案
  • D.空闲任务

答案:C。优先级反转的典型链条是:低优先级任务持锁进入临界区,被中优先级任务抢占,高优任务等锁。直接霸占资源的是持锁的低优任务,中优任务则趁虚抢占 CPU 加剧阻塞。优先级继承会让持锁低优任务临时获得高优优先级。

Q3. 关于二值信号量与计数信号量,下列说法正确?

单选题 · easy

  • A.二值信号量只能取 0 或 1← 正确答案
  • B.计数信号量计数值可长期为负
  • C.二值信号量可计数到任意值
  • D.计数信号量不能用于资源池

答案:A。二值信号量的计数只能是 0 或 1,表示“有无”;计数信号量计数值非负,表示剩余资源数,不可能长期为负。计数信号量很适合管理资源池,例如 5 个缓冲区就有初始值为 5 的信号量。

Q4. 互斥锁的优先级继承机制,核心做法是什么?

单选题 · mid

  • A.永久提高持锁任务优先级
  • B.降低高优任务优先级
  • C.持锁任务临时提升到阻塞方最高优先级← 正确答案
  • D.让中优任务继承低优任务优先级

答案:C。当低优任务持锁而高优任务等待时,系统把持锁任务的优先级临时提升到“等待该锁的任务中最高的优先级”,锁释放后恢复原优先级。这是缓解优先级反转的标准做法,而非永久提权。

Q5. 消息队列满时,发送任务在无超时情况下会?

单选题 · hard

  • A.直接丢弃消息并返回成功
  • B.立即返回失败错误码
  • C.阻塞直到队列有空间← 正确答案
  • D.覆盖队首最旧消息

答案:C。RTOS 消息队列的默认语义是满时阻塞发送者,直到空间释放或超时;丢弃和覆盖是特殊队列模式,立即失败属于非阻塞发送。无超时等同于无限期阻塞,等队列腾出位置。

Q6. 信号量计数为零时,执行 P 操作的任务会?

单选题 · easy

  • A.继续执行并创建负计数
  • B.进入阻塞态排队等待← 正确答案
  • C.转为空闲态运行其他代码
  • D.时钟中断强制重启

答案:B。信号量计数为零表示资源耗尽,P 操作不能再减计数,任务就地阻塞并加入等待队列,直到其他任务 V 操作释放令牌后被唤醒。计数非负是信号量的基本不变量,负计数是不允许出现的。

Q7. 下列哪项是互斥锁区别于二值信号量的重要能力?

单选题 · mid

  • A.支持多任务同时持有
  • B.支持优先级继承← 正确答案
  • C.支持外部中断释放
  • D.支持无阻塞尝试

答案:B。互斥锁特有的是优先级继承、递归获取与持有者归属;二值信号量不具备归属关系,也谈不上优先级继承。无阻塞尝试两种原语都可能提供,但这不是互斥锁的核心差异。

Q8. 任务在等待某信号量时被阻塞,其状态变化为?

单选题 · easy

  • A.运行态直接变空闲态
  • B.就绪态变挂起态
  • C.阻塞态变运行态
  • D.运行态变阻塞态← 正确答案

答案:D。任务原本运行,执行 P 操作发现计数为零,主动让出 CPU,从运行态转入阻塞态并加入等待队列。信号量 V 操作唤醒后,它先回就绪态,再被调度进入运行态。

Q9. 使用计数信号量管理 N 个资源池,下列描述正确的?

多选题 · mid

  • A.初始值应设为 N← 正确答案
  • B.初始值必须为 1
  • C.计数值表示剩余资源数← 正确答案
  • D.计数值表示已用资源数

答案:AC。计数信号量初始值等于资源总数 N,运行时计数值表示“还剩多少可用资源”。计数值为 0 时任务阻塞,V 操作归还资源后计数增加。已用资源数由 (N-计数值) 间接得到,而不是计数值本身。

Q10. 关于互斥锁优先级继承的运行过程,说法正确的?

多选题 · hard

  • A.高优任务永久降级
  • B.等待锁的低优任务临时提权← 正确答案
  • C.优先级提升无条件下发
  • D.锁释放后持锁任务恢复原优先← 正确答案

答案:BD。当高优任务因锁被低优任务持有而阻塞时,低优持锁任务被临时提升到高优任务的优先级,尽快执行完临界区并释放锁;锁释放后,持锁任务优先级自动恢复原来级别。提权必须发生在“持锁被高优等待”的条件下,并非无条件。