3.5.8 k_idle_task_init 函数详解
3.5.8 k_idle_task_init 函数详解
📚 本节导读
学习时长: 约 15 分钟
难度级别: ⭐⭐☆☆☆(初级)
前置知识:
- 3.5.5 k_sched_init 函数详解
- 3.5.7 k_recycle_task_init 函数详解
- 任务创建与调度基础知识
🎯 学习目标
- 理解空闲任务的设计目的
- 掌握
k_idle_task_init的初始化流程 - 理解空闲任务优先级为什么是
OS_TASK_PRIORITY_MAX - 1 - 了解 SMP 模式下多核空闲任务的创建
- 理解 tickless 低功耗模式与空闲任务的关系
一、函数概述
1.1 函数定位
k_idle_task_init 创建 idle 空闲任务,这是系统中优先级最低的任务。当所有其他任务都处于阻塞/挂起状态时,CPU 运行空闲任务,确保调度器始终有任务可执行。
1.2 基本信息
| 属性 | 值 |
|---|---|
| 函数名 | k_idle_task_init |
| 源文件 | kernel/source/os_idle.c(第 61-122 行) |
| 调用位置 | os_kernel_init() 第 278 行 |
| 空闲任务名 | "idle"(SMP 下为 "idle0", "idle1" ...) |
| 空闲任务栈大小 | 默认 512 字节(OS_IDLE_TASK_STACK_SIZE) |
| 空闲任务优先级 | OS_TASK_PRIORITY_MAX - 1(最低优先级) |
| 入口函数 | _k_idle_task_entry |
1.3 为什么需要空闲任务?
RTOS 调度器的基本规则是:CPU 必须始终有任务可运行。当所有用户任务都在等待事件(信号量、延时、消息队列等)时,如果没有空闲任务,调度器将面临"无任务可调度"的困境。
空闲任务充当兜底角色:
- 确保就绪队列永不为空
- 在空闲期间执行低功耗管理(tickless 模式)
- 可作为 CPU 使用率统计的参照
1.4 在 os_kernel_init 中的位置
void os_kernel_init(void)
{
_k_run_init_call(OS_INIT_LEVEL_PRE_KERNEL_1);
_k_show_sys_info();
k_tickq_init(); // 3.5.4
k_sched_init(); // 3.5.5
k_timer_module_init(); // 3.5.6
k_recycle_task_init(); // 3.5.7
k_idle_task_init(); // ← 这里(3.5.8)
_k_sys_task_init(); // 下一步(最后一个)
}空闲任务在回收任务之后、sys_task 之前创建。这三个任务构成了 OneOS 内核的 三大系统任务:
| 创建顺序 | 任务 | 优先级 | 作用 |
|---|---|---|---|
| 1 | recycle | 0(最低) | 回收已退出任务的资源 |
| 2 | idle | OS_TASK_PRIORITY_MAX - 1(最低) | CPU 空闲时运行 |
| 3 | sys_task | 较高 | 执行初始化调用链的后半段 |
注意优先级
recycle 和 idle 都是最低优先级(0 = OS_TASK_PRIORITY_MAX - 1 = 31),它们之间不会互相抢占。当两者同时就绪时,先就绪的先运行。
二、数据结构
2.1 静态变量
来源: kernel/source/os_idle.c 第 33-41 行
#ifdef OS_USING_SMP
OS_ALIGN(OS_ARCH_STACK_ALIGN_SIZE)
uint8_t gs_os_idle_stack[OS_SMP_MAX_CPUS][OS_ALIGN_UP(OS_IDLE_TASK_STACK_SIZE, OS_ARCH_STACK_ALIGN_SIZE)];
static os_task_dummy_t gs_os_idle_task[OS_SMP_MAX_CPUS];
#else
static OS_TASK_STACK_DEFINE(gs_os_idle_stack, OS_IDLE_TASK_STACK_SIZE);
static os_task_dummy_t gs_os_idle_task;
#endif| 变量 | 单核 | 多核(SMP) | 作用 |
|---|---|---|---|
gs_os_idle_stack | 单个栈数组 | OS_SMP_MAX_CPUS 个栈 | 空闲任务栈空间 |
gs_os_idle_task | 单个控制块 | OS_SMP_MAX_CPUS 个控制块 | 空闲任务控制块 |
SMP 模式
在 SMP(对称多处理)模式下,每个 CPU 核心都需要自己的空闲任务。例如 4 核系统会创建 idle0、idle1、idle2、idle3 四个任务,并通过 os_task_set_cpu_affinity 绑定到各自的 CPU。
三、函数详解
3.1 _k_idle_task_entry(空闲任务入口)
来源: kernel/source/os_idle.c 第 47-59 行
static void _k_idle_task_entry(void *arg)
{
OS_UNREFERENCE(arg);
while (1)
{
/* TODO: */
;
#ifdef OS_USING_TICKLESS_LPMGR
os_low_power_manager();
#endif
}
}这是整个 OneOS 中最简单的任务入口函数:
| 代码 | 作用 |
|---|---|
OS_UNREFERENCE(arg) | 消除"未使用参数"的编译警告 |
while (1) | 无限循环,永不退出 |
; | 空语句,CPU 在此空转 |
os_low_power_manager() | tickless 低功耗管理(可选) |
为什么空循环不浪费 CPU?
空闲任务只在没有其他任务就绪时才运行。如果有任何用户任务需要执行,调度器会立即抢占空闲任务。因此空循环不会浪费 CPU 时间——它只是填充了"本来就没有工作要做"的时间片。
如果启用了 OS_USING_TICKLESS_LPMGR,空闲任务会调用 os_low_power_manager() 让 CPU 进入低功耗休眠模式,而不是空转。
3.2 k_idle_task_init(单核模式)
来源: kernel/source/os_idle.c 第 97-119 行
/* Initialize idle task */
tid = os_task_create(&gs_os_idle_task,
OS_TASK_STACK_BEGIN_ADDR(gs_os_idle_stack),
OS_TASK_STACK_SIZE(gs_os_idle_stack),
OS_IDLE_TASK_NAME,
_k_idle_task_entry,
OS_NULL,
OS_TASK_PRIORITY_MAX - 1);
if (!tid)
{
OS_ASSERT_EX(0, "Why initialize idle task failed?");
}
/* Startup */
ret = os_task_startup(tid);
if (OS_SUCCESS != ret)
{
OS_ASSERT_EX(0, "Why startup idle task failed?");
}分步解析:
| 步骤 | 代码 | 作用 |
|---|---|---|
| 1 | os_task_create(...) | 创建空闲任务,绑定静态栈和控制块 |
| 2 | OS_ASSERT_EX(...) | 断言任务创建成功 |
| 3 | os_task_startup(tid) | 启动任务,加入就绪队列 |
| 4 | OS_ASSERT_EX(...) | 断言任务启动成功 |
参数详解:
| 参数 | 值 | 含义 |
|---|---|---|
| 任务控制块 | &gs_os_idle_task | 静态分配的 os_task_t |
| 栈地址 | gs_os_idle_stack | 静态分配的 512 字节栈 |
| 栈大小 | OS_IDLE_TASK_STACK_SIZE | 默认 512 字节 |
| 任务名 | "idle" | OS_IDLE_TASK_NAME 宏 |
| 入口函数 | _k_idle_task_entry | 空闲循环 |
| 入口参数 | OS_NULL | 无参数 |
| 优先级 | OS_TASK_PRIORITY_MAX - 1 | 最低优先级 |
优先级计算:
以 STM32F103ZET6 项目为例,OS_TASK_PRIORITY_MAX = 32:
优先级 31(= OS_TASK_PRIORITY_MAX - 1)→ idle 任务 ← 最低
优先级 30
...
优先级 1
优先级 0(= OS_TASK_PRIORITY_MAX - 1,当 MAX=32)→ recycle 任务注意
OneOS 中优先级数值越大表示优先级越低(与 FreeRTOS 相同,与 uC/OS 相反)。OS_TASK_PRIORITY_MAX - 1 是数值最大的优先级,即最低优先级。
3.3 k_idle_task_init(SMP 多核模式)
来源: kernel/source/os_idle.c 第 66-96 行
#ifdef OS_USING_SMP
char task_name[OS_NAME_MAX + 1];
int32_t i;
task_name[OS_NAME_MAX] = '\0';
for (i = 0; i < OS_SMP_MAX_CPUS; i++)
{
snprintf(task_name, OS_NAME_MAX, "idle%d", i);
tid = os_task_create(&gs_os_idle_task[i],
&gs_os_idle_stack[i],
OS_IDLE_TASK_STACK_SIZE,
task_name,
_k_idle_task_entry,
OS_NULL,
OS_TASK_PRIORITY_MAX - 1);
if (!tid)
{
OS_ASSERT_EX(0, "Why initialize idle task failed?");
}
os_task_set_cpu_affinity(tid, i);
/* Startup */
ret = os_task_startup(tid);
if (OS_SUCCESS != ret)
{
OS_ASSERT_EX(0, "Why startup idle task failed?");
}
}
#endif与单核模式的区别:
| 方面 | 单核 | SMP |
|---|---|---|
| 任务数量 | 1 个 "idle" | OS_SMP_MAX_CPUS 个 "idle0", "idle1" ... |
| 栈/控制块 | 单个变量 | 数组 [OS_SMP_MAX_CPUS] |
| CPU 亲和性 | 无 | os_task_set_cpu_affinity(tid, i) 绑定到指定核心 |
CPU 亲和性
os_task_set_cpu_affinity(tid, i) 将空闲任务 idle_i 绑定到 CPU 核心 i,确保每个核心都有自己专属的空闲任务,不会迁移到其他核心。
四、空闲任务与系统启动的关系
4.1 启动时序
os_kernel_init() os_kernel_start()
│ │
├─ k_recycle_task_init() ├─ PRE_KERNEL_2 初始化
│ └─ recycle 就绪(优先级 0) │
│ ├─ k_start()
├─ k_idle_task_init() │ └─ 启动第一个任务
│ └─ idle 就绪(优先级 31) │
│ ↓
├─ _k_sys_task_init() 调度器开始运行
│ └─ sys_task 就绪(优先级较高) │
│ ├─ sys_task 最先运行
└─ 返回 │ (优先级最高)
├─ sys_task 阻塞后
│ ├─ recycle 运行
│ └─ idle 运行(兜底)4.2 空闲任务永远不退出
与回收任务一样,空闲任务也是一个永不退出的系统任务。它的 while(1) 循环会一直运行直到系统断电。
💡 本节总结
重点回顾
- 设计目的:确保就绪队列永不为空,充当 CPU 空闲时的兜底任务
- 初始化流程:
os_task_create→os_task_startup,优先级为OS_TASK_PRIORITY_MAX - 1 - 入口函数:最简单的
while(1)空循环,可选 tickless 低功耗管理 - SMP 模式:每个核心一个空闲任务,通过 CPU 亲和性绑定
- 三大系统任务:recycle → idle → sys_task,按此顺序创建
关键符号说明
| 符号 | 来源 | 说明 |
|---|---|---|
gs_os_idle_task | os_idle.c | 空闲任务控制块(静态分配) |
gs_os_idle_stack | os_idle.c | 空闲任务栈空间 |
OS_IDLE_TASK_NAME | os_task.h | 空闲任务名 "idle" |
OS_IDLE_TASK_STACK_SIZE | Kconfig | 空闲任务栈大小,默认 512 |
OS_TASK_PRIORITY_MAX | oneos_config.h | 最大优先级数,默认 32 |
在 os_kernel_init 中的位置
os_kernel_init()
├─ _k_run_init_call(PRE_KERNEL_1)
├─ _k_show_sys_info()
├─ k_tickq_init() ← 3.5.4
├─ k_sched_init() ← 3.5.5
├─ k_timer_module_init() ← 3.5.6
├─ k_recycle_task_init() ← 3.5.7
├─ k_idle_task_init() ← 这里(3.5.8)
└─ _k_sys_task_init() ← 下一步(os_kernel_init 最后一步)📚 扩展阅读
- k_sched_init 函数详解 - 就绪队列与调度
- k_recycle_task_init 函数详解 - 回收任务
- _k_sys_task_init 函数详解 - 系统任务初始化(文档待创建)
下一步
接下来请学习:
- 3.5.9 _k_sys_task_init 函数详解 - 系统任务初始化