Zephyr 系统启动、线程与调度器
1. 从 Reset 到 z_cstart
各架构负责:
- 设置 CPU mode 和初始 stack;
- 必要时复制
.data、清理早期状态; - 初始化最基本的 exception/vector 环境;
- 调用通用 C 入口
z_cstart()。
具体 reset 入口位于 arch/<arch>/core/ 或 SoC startup。通用内核不假定某一种向量表或上下文格式。
2. z_cstart() 流程
kernel/init.c:z_cstart() 是通用启动主线:
1 | z_cstart() |
PRE_KERNEL 阶段仍在 interrupt/early boot stack 上运行,不能使用会阻塞或依赖完整调度器的内核服务。
3. Main thread
prepare_multithreading():
- 初始化 CPU 0;
- 为每个 CPU 创建 idle thread;
- 使用
z_setup_new_thread()创建z_main_thread; - 入口为
bg_thread_main(); - priority 为
CONFIG_MAIN_THREAD_PRIORITY; - stack 为
CONFIG_MAIN_STACK_SIZE; - 标记 main thread ready。
切换到 main thread 后执行:
1 | bg_thread_main() |
应用 main() 返回后,main thread 正常结束;其他线程和 idle 继续运行。
4. 初始化级别
include/zephyr/init.h 定义:
EARLYPRE_KERNEL_1PRE_KERNEL_2POST_KERNELAPPLICATIONSMP
SYS_INIT(fn, level, prio) 生成 struct init_entry 并放入:
1 | .z_init_<level><priority>_<subpriority>_ |
链接脚本按 section name 排序。z_sys_init_run_level() 遍历起止符号并调用:
- 系统 entry:
entry->init_fn.sys(); - device entry:设备初始化包装函数。
同一 level 的 priority 必须是 0–99 的十进制字面量或能直接展开为该字面量的宏,不能是算术表达式。
5. 线程对象
struct k_thread 的重要内容:
- 架构保存上下文;
struct _thread_base调度字段;- stack 信息;
- entry 参数和返回状态;
- timeout;
- resource pool;
- userspace permission/object metadata;
- thread name、custom data、runtime stats;
- SMP CPU mask/当前 CPU;
- join、abort、suspend 状态。
线程 ID k_tid_t 实际是 struct k_thread *。
6. 创建线程
静态线程
1 | K_THREAD_DEFINE(name, stack_size, entry, |
宏生成:
- stack;
struct k_thread;_static_thread_dataiterable entry。
启动时 z_init_static_threads() 先调用 z_setup_new_thread(),再按 delay 调度。
动态线程
1 | k_thread_create(thread, stack, stack_size, |
主要路径:
1 | k_thread_create |
架构层负责把初始 stack 构造成首次 context switch 后能进入 thread entry 的格式。
7. 优先级模型
数值越小,逻辑优先级越高:
1 | 负数:cooperative priorities |
宏:
1 | K_PRIO_COOP(x) |
Cooperative thread 在主动阻塞、yield、终止或遇到 meta-IRQ thread 前不会被普通线程抢占。Preemptible thread 可被更高优先级 ready thread 抢占。
8. Ready queue 实现
调度策略由 Kconfig 选择:
- simple/dumb queue:适合极少线程,线性查找;
- multi-queue:按优先级维护队列;
- scalable:红黑树,适合较多线程,插入/删除为 O(log n)。
抽象位于 kernel/priority_queues.c 和相关头文件。调度器不需要知道底层容器细节,只操作:
- queue thread;
- dequeue thread;
- best/next thread。
选择依据不是“算法越复杂越好”,而是线程数量、RAM/ROM 和调度延迟需求。
9. 调度核心
kernel/sched.c 关键函数:
z_ready_thread():把线程加入 ready queue;z_unready_thread():移除 ready 状态;next_up():选择下一线程;update_cache():更新 cached next thread;z_reschedule():判断是否需要切换;z_swap():完成通用调度状态转换并调用 arch switch;z_pend_curr():当前线程挂入 wait queue;z_unpend_first_thread():唤醒等待者;z_impl_k_yield():同优先级让出。
典型抢占路径:
1 | ISR/线程使高优先级线程 ready |
10. Wait queue 与阻塞
信号量、mutex、queue 等对象包含 wait queue。阻塞流程:
1 | 检查资源不可用 |
唤醒流程:
1 | 资源变为可用 |
11. Scheduler lock、IRQ lock 与 Spinlock
k_sched_lock():阻止当前 CPU 上普通抢占,不关闭中断;irq_lock():架构级屏蔽/锁定中断,临界区应极短;k_spin_lock():SMP 互斥并保存 IRQ 状态;- mutex:线程级可睡眠锁,支持 priority inheritance。
不能用 scheduler lock 替代 SMP 数据保护;另一个 CPU 仍可能并发访问。
12. 时间片
CONFIG_TIMESLICING 启用同优先级 preemptible threads 的轮转:
kernel/timeslicing.c管理 slice;CONFIG_TIMESLICE_SIZE设置 tick 数;CONFIG_TIMESLICE_PRIORITY决定应用范围;- thread 可配置自定义 time slice callback。
更高优先级唤醒仍立即抢占,时间片只解决同优先级公平性。
13. Timeout
kernel/timeout.c 管理全局 timeout queue。典型 timeout 来源:
k_sleep();- semaphore/mutex/poll 等限时等待;
- timer;
- delayed work;
- thread delayed start。
timeout 使用 tick 绝对/相对截止时间,并通过 delta queue 或配置的 timeout 容器管理。系统时钟 driver 到期后调用 sys_clock_announce(),推进 timeout 并唤醒对象。
Tickless 模式会让 timer driver 直接编程到最近 deadline,减少空闲 tick 中断。
14. 中断与调度
ISR 中可以调用标记为 ISR-safe 的 API,使线程 ready。是否在 ISR 退出时切换取决于:
- 是否有更高优先级 ready thread;
- scheduler lock;
- 当前线程 cooperative/preemptible;
- 架构的 interrupt exit/swap 实现。
ISR 不应调用可能睡眠的 API。k_is_in_isr() 可用于防御检查,但正确做法是按 API 文档设计上下文。
15. SMP
CONFIG_SMP 下:
_kernel.cpus[]保存每 CPU 当前线程;- shared ready queue 受 scheduler spinlock 保护;
- thread 可设置 CPU mask;
- IPI 通知其他 CPU reschedule;
- idle thread 每 CPU 一个;
next_up()需避免同一 thread 被多个 CPU 同时选中;- meta-IRQ threads 提供接近中断底半部的高优先级语义。
SMP 中即使本 CPU 不发生 switch,也可能需要发送 pending IPI。
16. 实时性注意事项
影响 worst-case latency 的因素:
- 长时间 IRQ lock/spinlock;
- cooperative 高优先级线程不阻塞;
- 大量同优先级 ready threads;
- logging immediate mode;
- flash erase/write;
- cache miss 和外部 memory;
- driver ISR 工作量;
- userspace syscall/MPU 切换;
- SMP lock contention。
实时性评估应测量最大值和尾延迟,而不只看平均 context-switch 时间。
正在加载留言…