3.5.3 _k_show_sys_info 函数详解
3.5.3 _k_show_sys_info 函数详解
📚 本节导读
学习时长: 约 10 分钟
难度级别: ⭐☆☆☆☆(入门级)
前置知识:
- 3.5.2.5 __driver_stm32_usart_early_driver_init 函数详解
- 了解条件编译
#ifdef基本用法
🎯 学习目标
- 理解
_k_show_sys_info函数的作用 - 了解
ONEOS_VERSION宏的定义 - 理解
os_kprintf在启动早期的输出路径 - 理解「调试模式」与「发布模式」的区别
一、函数概述
1.1 函数定位
_k_show_sys_info 是 os_kernel_init() 中执行的第 2 步,在 PRE_KERNEL_1 初始化完成后、调度器初始化之前,打印 OneOS 内核版本信息。
1.2 基本信息
| 属性 | 值 |
|---|---|
| 函数名 | _k_show_sys_info |
| 源文件 | kernel/source/os_startup.c(第 249-254 行) |
| 调用位置 | os_kernel_init() 第 262 行 |
| 作用域 | static,仅本文件可见 |
| 条件编译 | #ifdef OS_USING_KERNEL_DEBUG |
STM32F103ZET6 实际配置
OS_USING_KERNEL_DEBUG:已定义(在 projects/stm32f103zet6-atk-elite/oneos_config.h 第 36 行)ONEOS_VERSION:"OneOS Lite-V3.2.0"(在 kernel/include/os_version.h 第 37 行)
结论:条件满足,该函数会实际输出版本信息。
二、函数详解
2.1 完整代码
来源: kernel/source/os_startup.c(第 249-254 行)
static void _k_show_sys_info(void)
{
#ifdef OS_USING_KERNEL_DEBUG
os_kprintf("OneOS start, version:%s\r\n", ONEOS_VERSION);
#endif
}2.2 分步详解
第一步:条件编译检查
#ifdef OS_USING_KERNEL_DEBUG
// ...
#endif| 配置项 | 定义位置 | 作用 |
|---|---|---|
OS_USING_KERNEL_DEBUG | oneos_config.h(第 36 行) | 内核调试模式开关 |
- 调试模式(已定义):打印版本信息,方便开发者确认系统版本
- 发布模式(未定义):整个函数体为空,不输出任何内容
第二步:版本信息输出
os_kprintf("OneOS start, version:%s\r\n", ONEOS_VERSION);| 元素 | 说明 |
|---|---|
"OneOS start" | 提示系统正在启动 |
version:%s | 格式化占位符,替换为版本号 |
ONEOS_VERSION | 版本号宏,值为 "OneOS Lite-V3.2.0" |
\r\n | 回车换行,串口终端正确显示 |
实际输出效果(串口终端):
OneOS start, version:OneOS Lite-V3.2.02.3 ONEOS_VERSION 宏
定义位置: kernel/include/os_version.h(第 37 行)
#define ONEOS_VERSION "OneOS Lite-V3.2.0"版本号命名规则:OneOS-Vx.y.z
x:主版本号(Major)y:次版本号(Minor)z:修订号(Revision)
版本号的使用场景
ONEOS_VERSION 宏在两处被使用:
_k_show_sys_info()— 启动时打印(本节)- Shell 命令
version— 用户手动查询时打印(kernel/source/os_version.c第 39 行)
三、工作原理
3.1 为什么放在这里?
_k_show_sys_info() 在 os_kernel_init() 中的位置:
PRE_KERNEL_1 初始化(含串口早期初始化)
↓
_k_show_sys_info() ← 这里
↓
k_tickq_init() ← 滴答队列初始化
↓
k_sched_init() ← 调度器初始化
↓
...它放在 PRE_KERNEL_1 之后,是因为:
- PRE_KERNEL_1 中的
__driver_stm32_usart_early_driver_init已经保存了控制台串口句柄到console_uart - 此时
os_kprintf已经可以通过__os_hw_console_output输出到串口
它放在调度器初始化之前,是因为:
- 版本信息的打印不依赖调度器,不需要等待
- 尽早打印,方便开发者确认系统已启动到哪个阶段
3.2 os_kprintf 的早期输出路径
在启动早期,控制台设备尚未注册,os_kprintf 走的是直接硬件输出路径:
os_kprintf("OneOS start, version:%s\r\n", ONEOS_VERSION)
↓
os_vsnprintf() 格式化字符串到静态缓冲区 log_buff
↓
os_hw_console_output(log_buff) ← drivers/console/console.c
↓
os_console_get_device() 返回 OS_NULL ← 设备尚未注册
↓
__os_hw_console_output(log_buff) ← 硬件直接输出
↓
HAL_UART_Transmit(console_uart, ...) ← 通过 HAL 库发送
↓
串口输出 "OneOS start, version:OneOS Lite-V3.2.0"关键依赖
os_kprintf 能正常工作,依赖于 3.5.2.5 中 stm32_usart_early_probe 已经将 console_uart 设置为有效的 UART 句柄。如果 console_uart 仍为 OS_NULL,__os_hw_console_output 会直接返回,什么都不输出。
3.3 弱符号覆盖机制
__os_hw_console_output 的默认实现是一个弱符号(OS_WEAK)空函数:
// drivers/console/console.c 第 86-88 行
OS_WEAK void __os_hw_console_output(char *str)
{
}在 STM32 项目中,drivers/hal/st/drivers/drv_usart.c 提供了强符号覆盖:
// drv_usart.c 第 78-84 行
void __os_hw_console_output(char *str)
{
if (console_uart == OS_NULL)
return;
HAL_UART_Transmit(console_uart, (uint8_t *)str, strlen(str), STM32_UART_POLL_TIMEOUT_MS_PER_BYTE);
}链接时强符号会覆盖弱符号,因此实际执行的是 drv_usart.c 中的版本。
四、在启动流程中的位置
4.1 执行顺序
os_kernel_init() ← 3.5
↓
_k_run_init_call(OS_INIT_LEVEL_PRE_KERNEL_1) ← 3.5.2
↓
_k_show_sys_info() ← 这里(3.5.3)
↓
k_tickq_init() ← 滴答队列
↓
k_sched_init() ← 调度器
↓
...4.2 与其他版本查询方式的关系
| 方式 | 时机 | 路径 |
|---|---|---|
_k_show_sys_info() | 启动时自动 | os_kernel_init() → os_kprintf → 串口 |
Shell version 命令 | 用户手动输入 | sh_query_oneos_version() → os_kprintf → 控制台设备 |
两者最终都通过 os_kprintf 输出,但时机的不同决定了输出路径不同:
- 启动时:走
__os_hw_console_output直接硬件输出 - Shell 中:走
os_device_write_nonblock通过设备框架输出
💡 本节总结
重点回顾
- 函数作用:在调试模式下打印内核版本信息
"OneOS start, version:OneOS Lite-V3.2.0" - 条件编译:由
OS_USING_KERNEL_DEBUG控制,发布版本不输出 - 版本号来源:
ONEOS_VERSION宏,定义在kernel/include/os_version.h - 输出路径:启动早期走
__os_hw_console_output直接硬件输出 - 依赖关系:依赖 PRE_KERNEL_1 阶段完成
console_uart的初始化
关键符号说明
| 符号 | 来源 | 说明 |
|---|---|---|
OS_USING_KERNEL_DEBUG | oneos_config.h | 内核调试开关,控制是否输出 |
ONEOS_VERSION | os_version.h | 版本号字符串宏 |
__os_hw_console_output | drv_usart.c(强符号覆盖) | 早期硬件直接输出函数 |
os_hw_console_output | console.c | 控制台输出分发函数 |
📚 扩展阅读
- __driver_stm32_usart_early_driver_init 函数详解 - 了解
console_uart的初始化过程
下一步
接下来请学习:
- 3.5.4 k_tickq_init 函数详解 - 滴答队列初始化