嵌入式实时内核基础:任务、调度器、上下文切换、临界区与互斥
好,咱们今天聊点硬核的。实时内核,说白了就是操作系统的“微缩心脏”。你写裸机程序时,主循环里一个个函数轮着调,这叫“超级循环”。但到了复杂系统——比如同时要处理传感器、通信、显示——超级循环就扛不住了。这时候,你需要一个内核来帮你管“谁先跑、谁后跑、跑多久”。
我个人习惯把实时内核比作一个“交通调度中心”。任务就是路上的车,调度器就是红绿灯和交警,上下文切换就是车辆变道。嗯,咱们一个一个拆开看。
任务:内核里的“最小执行单元”
任务(Task),在RTOS里就是一段独立的、无限循环的代码。每个任务都有自己的栈、优先级和状态。
我刚开始用RTOS时犯过一个低级错误:把所有功能塞进一个任务里,结果系统响应慢得像蜗牛。后来才明白,任务拆分要遵循“高内聚、低耦合”原则——一个任务只干一件事。
任务通常有四种状态:
| 状态 | 说明 | 典型场景 |
|---|---|---|
| 运行态 | CPU正在执行该任务 | 任务获得CPU使用权 |
| 就绪态 | 任务可以运行,但CPU被别的任务占着 | 等待调度器分配CPU |
| 阻塞态 | 任务在等待某个事件(信号量、队列、延时) | 等待外设数据、等待定时器 |
| 挂起态 | 任务被显式暂停,不参与调度 | 调试、低功耗模式 |
调度器:谁先跑?跑多久?
调度器是内核的大脑。它决定哪个就绪任务获得CPU。常见的调度策略有两种:
- 优先级抢占式调度:高优先级任务随时可以打断低优先级任务。实时性高,但容易出“优先级反转”。
- 时间片轮转调度:同优先级任务轮流跑,每人分一个时间片。公平,但实时性一般。
我参与过一个工业控制项目,要求响应延迟不超过1ms。我们用了优先级抢占式调度,把电机控制任务设为最高优先级,通信任务设为中等,显示任务设为最低。效果不错,但调试时发现一个问题:通信任务偶尔会抢在电机任务之前跑——后来查出来是中断里释放了信号量,导致通信任务优先级被临时提升。这就是典型的“优先级反转”。
上下文切换:换人如换刀
上下文切换,就是暂停当前任务,保存它的现场(寄存器、栈指针、PC),然后恢复另一个任务的现场。这个过程必须快,越快越好。
你想想看,如果一次上下文切换要花100微秒,而你的系统有10个任务,每个任务跑1ms,那光切换就占了10%的CPU时间。这太浪费了。
典型的上下文切换流程:
- 触发异常或中断(比如PendSV)
- 保存当前任务的寄存器到它的栈中
- 更新当前任务的控制块(TCB)
- 从就绪队列中选出下一个任务
- 恢复新任务的寄存器
- 返回异常,继续执行新任务
// 伪代码:上下文切换核心逻辑
void PendSV_Handler(void) {
// 保存当前任务上下文
save_context(current_task->stack_ptr);
// 更新任务状态
current_task->state = READY;
// 选择下一个任务
next_task = scheduler_pick_next();
// 切换任务
current_task = next_task;
current_task->state = RUNNING;
// 恢复新任务上下文
restore_context(current_task->stack_ptr);
}
临界区与互斥:别让共享资源打架
临界区,就是一段不能被打断的代码。通常是因为这段代码在操作共享资源——比如全局变量、外设寄存器、内存池。
互斥,就是保证同一时间只有一个任务能进入临界区。实现互斥的手段有:
- 关中断:最简单粗暴,但关太久会影响实时性。
- 信号量:轻量级同步机制,适合短临界区。
- 互斥量:带优先级继承的信号量,能解决优先级反转。
- 锁:自旋锁、读写锁等,适合多核场景。
我建议:能用关中断解决的,就别用信号量。关中断的开销最小,但时间要短——一般不超过几十微秒。如果临界区太长,比如操作一个大数据结构,那就用互斥量。
| 方法 | 开销 | 适用场景 | 风险 |
|---|---|---|---|
| 关中断 | 极低 | 短临界区(<10μs) | 影响中断响应 |
| 信号量 | 低 | 任务间同步 | 优先级反转 |
| 互斥量 | 中 | 保护共享资源 | 死锁 |
| 关调度器 | 中 | 任务级临界区 | 影响任务调度 |
知识结构图:实时内核核心要素
下面这张图,把任务、调度器、上下文切换、临界区的关系串起来了。你可以把它当作本章的“思维导图”。
这张图里,四个模块是相互咬合的。任务管理提供“执行单元”,调度器决定“谁跑”,上下文切换负责“换人”,临界区与互斥保证“不打架”。少了任何一个,实时系统都转不起来。
嗯,这一章的内容就到这里。记住:任务要拆得合理,调度策略要选对,上下文切换要快,临界区要短。这四个点抓好了,你的RTOS应用就稳了一半。