reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
# µC/OS 与 SYS/BIOS:中断进入/退出 与分层调度
<CORE RULE>
这两个内核都把"中断参与调度"做成了**显式协议**,于是:
```
ISR 尾部突然发生上下文切换
→ 合理行为,不是异常
task 已 ready,却要等中断退出后才运行
→ 不是调度延迟
```
**判断调度时机时,必须把"中断进入/退出"这一段算进控制流。**
</CORE RULE>
## 一、Micrium µC/OS:ISR 必须通知内核
标准 ISR 形态:
```
保存寄存器
→ OSIntEnter() (或直接 OSIntNestingCtr++)
→ …处理…
→ OSIntExit()
恢复寄存器 → 从中断返回
```
- `OSIntEnter()` / `OSIntExit()` **必须成对**,且 **`OSIntEnter()` 不能被任务级代码调用**
- `OSIntEnter()` 递增全局**中断嵌套计数**,从而**保证 ISR 执行期间不做任务调度**
- 实现细节:若内核未在运行则直接返回;嵌套计数达到上限(250)也返回
- **直接递增计数可省去函数调用开销**,但要求"递增时中断已关闭"——**并非所有架构都成立**,需要自己确认
**`OSIntExit()` 的决策序列**(这是恢复控制流的关键):
```
内核未运行 → 返回
关中断 → 若嵌套计数为 0 → 开中断返回(防计数回绕)
递减嵌套计数 → 若仍 > 0(还有嵌套 ISR)→ 开中断返回
若调度器仍被锁 → 开中断返回
找出最高优先级就绪任务 → 与当前任务相同 → 开中断返回
否则更新切换计数并执行"中断级上下文切换"
```
- 即:**只有最后一个嵌套 ISR 退出时**才判断"是否需要切换到被 ISR 变为就绪的更高优先级任务"
- 在 Cortex-M 上,该切换通常**只是触发 PendSV**(最低优先级异常),**真正的切换推迟到所有 ISR 完成之后**
**所以**:`ready 但没运行` 常可解释为"还在 ISR 里 / 调度器被锁 / PendSV 尚未执行"。
## 二、TI SYS/BIOS:四级线程 + Swi 禁用会连带禁用 Task 调度
线程层次(优先级由高到低):
```
Hwi(硬件中断) > Swi(软件中断) > Task > Idle
```
- **Swi 不能阻塞、总是运行到完成**;Task 可以阻塞,且会被任何更高优先级的线程抢占
- Swi 与 Task 都有优先级级别(数值越大优先级越高,0 最低)
**关键的连带效应**:
- **`Swi_disable()` 会同时禁用 Task 调度器**——等价于同时调用"禁 Task"与"禁 Swi"
- 因此:`Swi_post()` 在临界区中不会立即运行;即使 `Semaphore_post()` 让更高优先级任务就绪,**当前任务也不会在 `Swi_restore()` 之前被抢占**
- `Swi_restore()` 在**此前 Task 调度器未被禁用**的前提下,会重新启用并进入 Task 调度器
- **硬性约束**:Swi 调度器被禁用时**绝不能调用阻塞 API**(如信号量等待/门互斥进入),否则会"不可恢复地损坏"Task 调度器内部状态,行为"不可预测且通常是灾难性的"
- 禁用某一层**不影响更高层抢占**(`Swi_disable()` 不阻止 Hwi 抢占)
**栈模型**:
- **Hwi 与 Swi 共用同一个系统中断栈(ISR 栈)**,一个程序一个;每个 Task 有私有栈
- 若系统里没有 Task,所有线程共用一个系统栈;默认系统栈大小 4096 字节
- **每多一个嵌套的 Hwi/Swi 优先级层次,栈需求就上升**;Swi 栈需求与所用 Swi 优先级数量成正比
**所以**:
```
高优先级 Task 已就绪却不切换
→ 先查 Swi 调度器是否被禁用(它会连带禁 Task 调度)
→ 不要先找死锁
```
## 三、µC/OS 的其余高频误判
**调度器锁与挂起是可嵌套的**:
- `OSSchedLock()` 是可**嵌套**的:即使退出了 ISR,**只要调度器仍被锁住就不会重调度**;必须成对解除
- `OSTaskSuspend()` 同样可嵌套——**长期不就绪的高优先级任务先查这两者的嵌套层级**,再谈优先级反转
**信号量 vs 互斥量**:
- 多个任务可同时共享的资源 → **信号量**;**独占资源 → 互斥量**(互斥量实现会处理优先级反转)
- 因此出现"低优先级任务影响高优先级任务"时,**先确认作者是不是把信号量当互斥量用了**
**软件定时器回调运行在定时器任务上下文**(这是很值钱的一条):
- 回调**在 OS 内部的定时器任务上下文中执行**——**既不是中断,也不是创建/启动该定时器的任务**
- **用的是定时器任务的栈**,所以定时器任务的栈要按回调需求配足
- **回调里绝不能做阻塞/等待**(延时、任何 `OS???Pend()`)——因为所有回调在定时器任务里**串行执行**,阻塞一个回调会把整个定时器子系统卡住
- 所以:
```
TimerCallback() → 内部有 blocking wait
→ 高价值的 bug 信号(不是"偶发卡顿")
```
**PendAbort:被唤醒 ≠ 拿到资源**:
- 中止等待(针对任务内建消息队列或普通消息队列)的作用是**中止等待并让任务就绪**,而**不是正常投递**
- **只能由任务调用(不能从 ISR 调)**;指针为 NULL 或指向调用者自己是无效用法
- 被唤醒的任务会**带着"等待被中止"的错误码返回**
**所以**:任务被唤醒、但队列/信号量里并没有新消息——先看是不是中止等待路径,**"被唤醒"与"成功获取资源"是两件事**。
## 决策树
```
拿到 µC/OS 或 SYS/BIOS 目标
├─ 调度时机对不上
│ ├─ µC/OS:查 OSIntNestingCtr(是否还在 ISR 里)、调度器锁、PendSV 是否已执行
│ ├─ SYS/BIOS:查 Swi 调度器是否被禁用(连带禁用 Task 调度)
│ └─ 别先判"调度延迟/死锁"
├─ ISR 内调用内核 API
│ ├─ µC/OS:是否成对调用 OSIntEnter/OSIntExit
│ ├─ SYS/BIOS:是否误用阻塞 API(Swi 禁用期间调用会损坏调度器状态)
│ └─ 注意 SYS/BIOS 的 Swi 不能阻塞
├─ 栈相关异常
│ ├─ SYS/BIOS:Hwi 与 Swi 共用 ISR 栈;嵌套层次与 Swi 优先级数影响栈需求
│ └─ 默认系统栈 4096 字节,注意配置项
└─ 看到"直接递增嵌套计数"的写法
└─ 只在"递增时中断已关闭"的架构上安全,需逐架构确认
```
## 工具与验证
- 中断协议:ISR 的进入/退出调用是否成对;嵌套计数的读写点
- 调度禁用:调度器锁、Swi/Task 禁用标志与恢复点
- 栈:系统(ISR)栈与任务栈的划分、嵌套层次与栈配置
- 验证:能同时说清「当前是否在 ISR 上下文 + 哪一层的调度被禁用 + 切换在何时真正发生」
## 该平台的坑(汇总)
- **把 ISR 尾部的切换当异常**:那是中断退出时的正常调度决策
- **忽略嵌套计数**:只有最后一个嵌套 ISR 退出才判断切换
- **以为 PendSV 触发就等于已切换**:真正的切换在所有 ISR 完成后
- **在 µC/OS 里让任务级代码调用 `OSIntEnter()`**:协议禁止
- **照搬"直接递增计数"的优化**:只在特定架构安全
- **忘记 `Swi_disable()` 会连带禁用 Task 调度**:这是 SYS/BIOS 最反直觉的一条
- **在 Swi 调度器禁用期间调用阻塞 API**:会不可恢复地损坏调度器状态
- **按"每个线程独立栈"估算 SYS/BIOS 栈需求**:Hwi 与 Swi 共用 ISR 栈