8.1 KiB
8.1 KiB
eCos:ISR / DSR / thread、CDL 配置系统、HAL 与启动
eCos 有两条同样重要的东西,**只掌握其中一条都会误判**:运行期:ISR → DSR → thread 三层(中断模型)
构建期:CDL 配置系统决定编进什么、向量在哪、谁先跑
同一份源码在不同配置下的产物差异极大——所以顺序是:
先确认配置(CDL),再谈代码
</CORE RULE>
一、ISR 与 DSR 的分工
- ISR 返回时若带"需要调用 DSR"的位,系统会调用关联的 DSR(Deferred Service Routine)
- 若调度器未被锁定,DSR 立即运行;若被中断的线程恰在内核调用里锁了调度器,DSR 会被推迟到解锁之后
- 这个推迟正是 DSR 能安全做某些内核调用的原因——例如 post 一个信号量、signal 一个条件变量
二、同步级别是 API 的属性
| 级别 | 谁可以调用 |
|---|---|
| Thread | 仅线程代码 |
| DSR | DSR 或线程代码 |
| ISR | ISR、DSR 或线程代码 |
调用点落在哪一级,决定了哪些 API 是合法的。
三、三种同步机制
| 机制 | 作用 | 谁能用 |
|---|---|---|
| 与 ISR 同步(禁中断 + SMP 下自旋锁) | 阻止临界区内 ISR 运行 | 必须短且少用 |
| 与 DSR 同步 | 阻止临界区内 DSR 运行(内核里等价于取调度器锁) | DSR 与线程可用;ISR 不允许使用 |
| 与线程同步(互斥量、条件变量) | 线程级阻塞与唤醒 | 只有线程可以 lock 互斥量与 wait 条件变量;DSR 可以 signal 条件变量 |
数据保护按"谁会碰它"分层:被 ISR 访问的数据在任何层级(包括 ISR 内部)都要用 ISR 锁/自旋锁保护;DSR 与线程共享的用 DSR 锁;只有线程访问的用互斥量。
四、三种典型驱动模型
- 全部处理放在 ISR(DSR 只 signal 条件变量唤醒线程)
- 处理推迟到 DSR(DSR 可选地 signal 条件变量并解屏蔽中断)
- 处理推迟到线程(DSR 只 signal,线程随后获得完整内核设施)
五、CDL:配置系统决定一切
配置由 CDL 定义驱动,选项决定"编进什么"。几个有逆向价值的类别:
- 通信通道选择(通道数量、调试通道、控制台通道及其波特率、控制台默认通道)
- 使用 ROM 监视器时这些通道选项通常失效——由监视器选择通道;应用可通过配置项声明自己接管通信
- 调试/GDB 桩的包含与否直接影响镜像内容与异常处理去向
- 构建目标(ROM / ROMRAM / RAM 等)决定镜像是否需要在启动时重定位
逆向含义:拿到一个 eCos 镜像,"里面有什么"是配置的产物,不是源码的自然结果。先把配置项与编入项对上,再解释代码。
六、HAL:向量表与 VSR
- 向量表的位置完全由内存映射决定:由内存布局工具决定,并体现在布局描述文件里
- 启动时初始化向量表
- 同步异常的向量项大多指向一个默认异常向量:它保存 CPU 状态并调用异常处理器,后者按配置路由到调试桩或路由到 eCos
- 由 ROM 监视器加载的应用,其向量项指向监视器自己的异常向量——所以异常会进监视器(例如调试器),除非应用显式替换该项
- 异常向量的偏移由 HAL 头文件里的编号定义
七、虚拟向量:ROM 监视器给 RAM 应用提供的服务表
- 在静态地址处有一张表(几十个向量),指向服务函数或数据,让 ROM 配置(如 RedBoot)为单独链接的 RAM 应用提供服务
- 服务含:通信通道(控制台/调试)、微秒延时、复位、缓存刷新、调试/控制台通道切换、调试系统调用(线程信息)、flash 配置访问
- 由 ROM 监视器或按策略由独立/RAM 配置初始化;无论用不用都保留固定的字节数
- 代价是间接层(体积/性能开销,并多一个失败点);收益是应用可以不含调试桩也能被调试
八、启动顺序(RedBoot / 典型 ROM 配置)
复位入口
→ 平台设置宏:禁中断与缓存、初始化内存控制器与时钟、建立并开启 MMU
(ROMRAM 情形还要重定位镜像)
→ HAL 启动:在 0 号位置放置向量指令、复制 .data、设栈、清 .bss
→ 硬件初始化
→ (含调试桩则)初始化调试桩
→ 中断(ctrl-C 类)初始化
→ 【调用构造函数】
→ 进入 cyg_start
构造函数在硬件/桩/中断初始化之后、进入主启动之前运行。
九、构造函数与内存布局的一个真实坑
向量表所在区域如果与构造函数区域重叠,初始化会把构造函数冲掉——曾出现过"预期的构造函数在某个地址上,结果被初始化向量表时覆盖"的实例。
所以:向量表位置必须按目标内存映射决定,且如果需要被初始化,就不能放在 flash 里。
十、调试与 GDB 桩
- 调试桩在 HAL 启动阶段被初始化;异常处理器在含桩时进 GDB
- ROM 监视器通常包含 GDB 协议支持,可以关掉以节省代码与内存
- 虚拟向量让"不含桩的应用"也能被调试;但某些较老的 ROM 监视器在异步调试中断上会失败,除非应用里也配了调试支持
- 相关配置项控制"是否构建带 GDB 的监视器"以及"是否包含桩"
十一、平台差异与可配置性
- eCos 覆盖多种架构的 HAL,不同架构的 HAL 在向量表、启动序列、缓存与 MMU 处理上不同
- 调度器、定时器、同步原语都受配置影响;"这版 eCos 的线程模型是怎样的"要由配置回答
- 内存池与堆的配置同样属于构建期选择
决策树
拿到 eCos 目标
├─ 先确认配置(CDL)
│ ├─ 构建目标(ROM/ROMRAM/RAM)→ 是否启动时重定位
│ ├─ 是否含调试桩 → 异常去向
│ └─ 通道/控制台/调度器等选项 → 编入了什么
├─ 看到一个很短的中断处理函数
│ └─ 真正工作可能在 DSR(或第三个线程)
├─ 看到一个没有线程入口却反复执行的函数
│ └─ 可能是 DSR
├─ 判断某个内核调用是否合法
│ └─ 查它的同步级别(Thread / DSR / ISR)
├─ 异常去向与预期不符
│ ├─ 是否含调试桩?
│ ├─ 向量项指向 eCos 还是 ROM 监视器?
│ └─ 应用是否替换了向量项
├─ 构造函数"没运行"
│ └─ 查向量表所在区域是否与构造函数区域重叠(会被初始化冲掉)
└─ 找不到某个功能
└─ 可能是配置里就没编进去
工具与验证
- 配置:CDL 选项与编入项、构建目标、调试桩是否包含
- 分层:ISR 返回值(是否调度 DSR)、DSR 注册与调用点、各调用的同步级别
- HAL:向量表位置(布局文件)、向量项指向、异常处理器路由
- 启动:复位入口 → 平台设置 → HAL 启动 → 构造函数 → 主启动
- 验证:能同时说清「这份镜像是哪套配置构建的 + 中断处理分布在哪几层 + 异常会去哪里」
该平台的坑(汇总)
- 把 ISR 当完整的中断处理:工作可能在 DSR,甚至在第三个线程里
- 把 DSR 当普通线程回调:它有同步级别与调度约束,且会被调度器锁推迟
- 在 ISR 里套用 DSR 级同步的假设:ISR 不允许使用 DSR 锁
- 以为 DSR 可以像线程一样等待:能 signal 条件变量,不能 wait
- 按"中断发生时刻"推断时序:DSR 的运行时机受调度器锁影响
- 不看配置就解释代码:CDL 决定编入项、通道、调试桩、构建目标
- 把异常去向当固定:取决于是否含桩、向量项指向谁、应用是否替换
- 忽略向量表与构造函数区域冲突:构造函数会被初始化覆盖
- 以为 ROM 监视器加载的应用异常会进 eCos:默认进监视器
- 把虚拟向量当纯开销:它让无桩应用也能被调试,是有意的权衡