# eCos:ISR / DSR / thread、CDL 配置系统、HAL 与启动 eCos 有两条同样重要的东西,**只掌握其中一条都会误判**: ``` 运行期:ISR → DSR → thread 三层(中断模型) 构建期:CDL 配置系统决定编进什么、向量在哪、谁先跑 ``` **同一份源码在不同配置下的产物差异极大**——所以顺序是: ``` 先确认配置(CDL),再谈代码 ``` ## 一、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 锁;只有线程访问的用互斥量。 ## 四、三种典型驱动模型 1. **全部处理放在 ISR**(DSR 只 signal 条件变量唤醒线程) 2. **处理推迟到 DSR**(DSR 可选地 signal 条件变量并解屏蔽中断) 3. **处理推迟到线程**(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**:默认进监视器 - **把虚拟向量当纯开销**:它让无桩应用也能被调试,是有意的权衡