第 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_FOREVERK_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=0Thread B
等待SemaphoreThread A
完成工作 ↓ give SemaphoreSemaphore变成
1Thread 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
立即失败。
八、释放