☰
Zephyr应用: 11-Semaphore
2026/9/29 6:16:08 网站建设 项目流程

第 11 课:Semaphore(信号量)

摘要
本课深入讲解 Zephyr RTOS 中线程同步的核心机制——Semaphore(信号量)。你将掌握二值信号量与计数信号量的区别,学会K_SEM_DEFINE()、k_sem_take()、k_sem_give()等核心 API,并通过生产者-消费者、GPIO 中断唤醒线程等实战示例,理解信号量在事件通知与资源计数中的典型应用,告别轮询带来的 CPU 浪费。学完本课,你就能在嵌入式多线程项目中正确选用信号量,为后续学习 Mutex 与 Message Queue(消息队列)打下坚实基础。

关键词:Zephyr RTOS、Semaphore(信号量)、线程同步、Binary Semaphore(二值信号量)、Counting Semaphore(计数信号量)
目标
学会 Zephyr 中线程同步最重要的机制——Semaphore(信号量)。

完成本课以后,你应该知道:
什么是 Semaphore
Binary Semaphore(二值信号量)
Counting Semaphore(计数信号量)
k_sem_init()
k_sem_take()
k_sem_give()
K_FOREVER
K_NO_WAIT

一、为什么需要 Semaphore?
假设有两个线程。

Thread A ↓ 采集ADC ↓ 处理完成
Thread B ↓ 等待ADC完成 ↓ 发送数据

如果没有同步:

Thread B while(1){if(adc_ready)send();}

这叫
Polling(轮询)

缺点:

浪费CPU 延迟高 不优雅

所以需要:

Thread A 完成工作 ↓

通知 Thread B

这个通知就是:
Semaphore

二、Semaphore 工作原理
可以理解成一个「令牌(token)」,用于控制对共享资源的访问。
初始:

Semaphore=0

Thread B

等待Semaphore

Thread A

完成工作 ↓ give Semaphore

Semaphore变成

1

Thread B立刻被唤醒。

流程:

Thread A give()│ ▼ Semaphore++ │ ▼ Thread B take()

继续运行Thread B
take()

继续运行 下面用时序图更直观地展示 `k_sem_give()` 唤醒线程的完整过程: ```mermaid sequenceDiagram participant A as Thread A participant S as Semaphore participant B as Thread B Note over B: 调用 k_sem_take() 等待 B->>S: k_sem_take(&my_sem, K_FOREVER) Note over S: 计数为 0,B 阻塞等待 A->>S: k_sem_give(&my_sem) Note over S: 计数 0 → 1 S-->>B: 唤醒等待中的线程 B B->>S: k_sem_take() 获取令牌 Note over S: 计数 1 → 0 Note over B: 继续运行

时序说明:线程 B 先调用k_sem_take()等待令牌,此时计数为 0,B 进入阻塞;线程 A 完成工作后调用k_sem_give(),信号量计数从 0 增加到 1,内核随即唤醒等待中的线程 B;B 被唤醒后通过k_sem_take()取走令牌,计数回到 0,B 继续执行后续逻辑。整个过程实现了「A 通知、B 等待」的线程同步。

三、Binary Semaphore

只有:

0

或者

1

类似:

门关着0
门打开1

这是 RTOS 中最常见的同步方式。

四、Counting Semaphore
例如:
最大值5

Semaphore0↓1↓2↓3↓4↓5

可以表示:
资源数量。

例如:

USB Buffer 剩余5个

每拿一个:

take()↓4

释放:

give()↓5

五、创建 Semaphore

Zephyr:

#include<zephyr/kernel.h>K_SEM_DEFINE(my_sem,0,1);

解释:

名字 my_sem

初始值:
0

最大值:
1

所以:
Binary Semaphore

如果:
K_SEM_DEFINE(my_sem, 0, 5);

就是:
Counting Semaphore

六、动态初始化
也可以:

structk_semsem;k_sem_init(&sem,0,1);

参数:

初值0

最大值

1

七、等待 Semaphore

线程等待:

k_sem_take(&my_sem,K_FOREVER);

意思:

一直等

直到:

别人give()

也可以:

k_sem_take(&my_sem,K_SECONDS(2));

等待:
2秒

超时:
返回错误

立即返回:

k_sem_take(&my_sem,K_NO_WAIT);

如果没有:
Semaphore
立即失败。

八、释放

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询