3.5.11 POST_KERNEL 阶段详解
2026/4/29大约 4 分钟启动流程初始化POST_KERNEL工作队列日志系统板级初始化
3.5.11 POST_KERNEL 阶段详解
📚 本节导读
学习时长: 约 20 分钟
难度级别: ⭐⭐⭐☆☆(中级)
前置知识:
- 3.5.10 os_kernel_start 与 k_start 详解
- 3.5.9 _k_sys_task_init 函数详解
- 3.5.1 _k_run_init_call 函数详解
🎯 学习目标
- 理解 POST_KERNEL 级别的定位和调用时机
- 掌握 sys_task 中 4 个 POST_KERNEL 函数的职责
- 理解工作队列、日志系统、板级后初始化的作用
- 理解子级别(1.1, 1.2)的执行顺序
一、阶段概述
1.1 阶段定位
POST_KERNEL 是调度器启动后执行的第一个初始化级别。它在 _k_sys_task_entry 中调用,此时 sys_task 已经作为最高优先级任务运行,可以使用所有内核服务(如 os_kprintf、os_mutex 等)。
1.2 基本信息
| 属性 | 值 |
|---|---|
| 初始化级别 | OS_INIT_LEVEL_POST_KERNEL(值 3) |
| 调用位置 | _k_sys_task_entry 第 192 行 |
| 调用时机 | 调度器启动后,sys_task 中 |
| 前置条件 | 调度器已运行,内核服务可用 |
| 总函数数 | 5 个(1 个标记 + 4 个实际函数) |
1.3 从 project.map 看实际注册
.init_call.3."" → post_kernel_start ← 标记函数(os_startup.o)
.init_call.3.1 → os_sys_workqueue_init ← 系统工作队列初始化
.init_call.3.1 → dlog_init ← 日志系统初始化
.init_call.3.2 → dlog_console_backend_init ← 日志控制台后端初始化
.init_call.3.2 → board_post_init ← 板级后初始化二、执行顺序
2.1 子级别执行顺序
POST_KERNEL 级别内:
""(空子级别)→ post_kernel_start(标记函数)
3.1 → os_sys_workqueue_init, dlog_init
3.2 → dlog_console_backend_init, board_post_init2.2 依赖关系
os_sys_workqueue_init ← 无依赖,可独立初始化
dlog_init ← 无依赖,可独立初始化
dlog_console_backend_init ← 依赖 dlog_init(日志框架就绪)
board_post_init ← 依赖 os_sys_workqueue_init(可能需要工作队列)三、各函数详解
3.1 os_sys_workqueue_init — 系统工作队列初始化
| 属性 | 值 |
|---|---|
| 源文件 | kernel/source/os_workqueue.c |
| 子级别 | 3.1 |
| 作用 | 创建系统工作队列 sys_work |
系统工作队列允许将工作项延迟到工作线程中执行。典型使用场景:
// 定义工作项
static void my_work_handler(void *param)
{
os_kprintf("work executed in workqueue thread\r\n");
}
// 提交工作项到系统工作队列
os_workqueue_submit(OS_NULL, my_work_handler, OS_NULL, 0);3.2 dlog_init — 日志系统初始化
| 属性 | 值 |
|---|---|
| 源文件 | components/dlog/source/dlog.c |
| 子级别 | 3.1 |
| 作用 | 初始化 OneOS 日志系统框架 |
dlog 是 OneOS 的日志系统,提供带标签和级别的日志输出。初始化后,组件可以使用 LOG_I、LOG_D、LOG_E 等宏输出日志。
3.3 dlog_console_backend_init — 日志控制台后端初始化
| 属性 | 值 |
|---|---|
| 源文件 | components/dlog/source/console_backend.c |
| 子级别 | 3.2 |
| 作用 | 注册日志的控制台输出后端 |
日志系统(dlog)支持多种输出后端(控制台、文件、网络等)。dlog_console_backend_init 注册控制台作为默认后端,日志通过 os_kprintf 输出到串口。
为什么在 3.2 而不是 3.1?
dlog_console_backend_init 依赖 dlog_init(日志框架先就绪),所以放在更高的子级别 3.2。
3.4 board_post_init — 板级后初始化
| 属性 | 值 |
|---|---|
| 源文件 | drivers/hal/st/drivers/drv_common.c |
| 子级别 | 3.2 |
| 作用 | 板级后初始化(STM32 默认空实现) |
在 STM32F103ZET6 项目中,board_post_init 是空实现:
static int board_post_init(void)
{
return 0;
}用户可在此处添加需要在调度器启动后执行的板级操作。
四、启动流程上下文
4.1 在 sys_task 中的位置
void _k_sys_task_entry(void *arg)
{
/* post kernel init call */ // ← 本节
_k_run_init_call(OS_INIT_LEVEL_POST_KERNEL);
// 执行: post_kernel_start → os_sys_workqueue_init → dlog_init
// → dlog_console_backend_init → board_post_init
#ifdef OS_USING_SMP
_k_run_init_call(OS_INIT_LEVEL_SMP);
#endif
_k_run_init_call(OS_INIT_LEVEL_PRE_DEVICE); // → 3.5.12
_k_run_init_call(OS_INIT_LEVEL_DEVICE); // → 3.5.13
_k_run_init_call(OS_INIT_LEVEL_COMPONENT); // → 3.5.14
_k_run_init_call(OS_INIT_LEVEL_APPLICATION); // → 3.5.15
app_entry(); // → main()
}4.2 完整启动流程
os_kernel_start() → k_start() → PendSV → sys_task 运行
↓
_k_sys_task_entry()
├─ POST_KERNEL 初始化 ← 本节(3.5.11)
├─ SMP 初始化(可选)
├─ PRE_DEVICE 初始化 ← 3.5.12
├─ DEVICE 初始化 ← 3.5.13
├─ COMPONENT 初始化 ← 3.5.14
├─ APPLICATION 初始化 ← 3.5.15
└─ app_entry() → main()💡 本节总结
重点回顾
- POST_KERNEL 是调度器启动后第一个初始化级别:此时内核服务已可用
- 4 个实际函数:工作队列初始化、日志框架初始化、日志后端初始化、板级后初始化
- 子级别反映依赖:3.1 先于 3.2,
dlog_console_backend_init依赖dlog_init - board_post_init 在本项目中为空:用户扩展点,可添加板级后初始化操作
关键函数速查
| 函数 | 子级别 | 职责 |
|---|---|---|
post_kernel_start | "" | 标记函数 |
os_sys_workqueue_init | 3.1 | 创建系统工作队列 |
dlog_init | 3.1 | 初始化日志框架 |
dlog_console_backend_init | 3.2 | 注册控制台日志后端 |
board_post_init | 3.2 | 板级后初始化(空实现) |
下一步
接下来请学习:
- 3.5.12 PRE_DEVICE 阶段详解 - 设备注册前的驱动初始化