3.5.5 k_sched_init 函数详解
2026/4/29大约 7 分钟启动流程初始化调度器就绪队列位图优先级
3.5.5 k_sched_init 函数详解
📚 本节导读
学习时长: 约 25 分钟
难度级别: ⭐⭐⭐⭐☆(高级)
前置知识:
- 3.5.4 k_tickq_init 函数详解
- 双向链表基本操作
- 位运算基础(
os_ffs查找第一个置位 bit)
🎯 学习目标
- 理解就绪队列(Ready Queue)的数据结构设计
- 掌握位图优先级调度算法(Bitmap Priority Scheduling)
- 理解
k_sched_init的初始化过程 - 了解
k_readyq_put/k_readyq_remove的就绪队列操作 - 理解全局变量
g_os_current_task、g_os_next_task、g_os_high_task的作用
一、函数概述
1.1 函数定位
k_sched_init 是 os_kernel_init() 中执行的第 4 步,负责初始化调度器的核心数据结构——就绪队列(Ready Queue)。
1.2 基本信息
| 属性 | 值 |
|---|---|
| 函数名 | k_sched_init |
| 源文件 | kernel/source/os_sched.c(第 727-742 行) |
| 调用位置 | os_kernel_init() 第 266 行 |
| 核心数据结构 | struct readyq_bitmap(就绪队列位图) |
1.3 什么是调度器?
调度器是 RTOS 的"大脑",负责决定哪个任务获得 CPU 使用权。OneOS 采用基于优先级的抢占式调度(Priority-based Preemptive Scheduling):
- 每个任务有一个优先级(0 最高,
OS_TASK_PRIORITY_MAX - 1最低) - 任何时候,CPU 运行优先级最高的就绪任务
- 当更高优先级任务就绪时,立即抢占当前任务
STM32F103ZET6 实际配置
OS_TASK_PRIORITY_MAX:32(在 oneos_config.h 第 18 行)OS_USING_SMP:未定义(单核芯片)
结论:本项目使用单核 32 优先级调度,本节以单核模式为主讲解。
二、函数详解
2.1 完整代码
来源: kernel/source/os_sched.c(第 727-742 行)
void k_sched_init(void)
{
#ifdef OS_USING_SMP
uint8_t i;
_k_readq_bmap_init(&gs_os_global_readyq);
for (i = 0; i < OS_SMP_MAX_CPUS; i++)
{
_k_readq_bmap_init(&gs_os_aff_readyq[i]);
}
#else
_k_readq_bmap_init(&gs_os_readyq);
#endif
return;
}2.2 分步详解
第一步:全局变量定义
调度器依赖以下全局变量(os_sched.c 第 78-87 行):
os_task_t *g_os_current_task = OS_NULL; // 当前正在运行的任务
os_task_t *g_os_next_task = OS_NULL; // 下一个要运行的任务(调度时使用)
os_task_t *g_os_high_task = OS_NULL; // 就绪队列中最高优先级的任务(缓存)
int16_t g_os_sched_lock_cnt = 0; // 调度锁计数器
static struct readyq_bitmap gs_os_readyq; // 全局就绪队列| 变量 | 说明 |
|---|---|
g_os_current_task | 当前 CPU 正在执行的任务,初始为 OS_NULL |
g_os_next_task | 调度切换的目标任务,k_start() 中赋值 |
g_os_high_task | 就绪队列中最高优先级任务,避免每次遍历查找 |
g_os_sched_lock_cnt | 调度锁计数,>0 时禁止调度 |
gs_os_readyq | 核心数据结构——就绪队列位图 |
第二步:调用 _k_readq_bmap_init
_k_readq_bmap_init(&gs_os_readyq);这是核心初始化函数(os_sched.c 第 91-108 行):
OS_INLINE void _k_readq_bmap_init(struct readyq_bitmap *readyq)
{
uint32_t i;
#if OS_TASK_PRIORITY_MAX > 32
readyq->priority_group_bmap = 0;
memset(&readyq->priority_bmap[0], 0, sizeof(readyq->priority_bmap));
#else
readyq->priority_bmap = 0; // 位图清零
#endif
for (i = 0; i < OS_TASK_PRIORITY_MAX; i++)
{
os_list_init(&readyq->priority_list_array[i]); // 每个优先级链表初始化
}
return;
}初始化后的内存布局(STM32F103,32 优先级):
readyq_bitmap:
priority_bmap = 0x00000000 ← 32 位位图,每个 bit 对应一个优先级
priority_list_array[0] ←→ 自身 ← 优先级 0 的链表(空)
priority_list_array[1] ←→ 自身 ← 优先级 1 的链表(空)
priority_list_array[2] ←→ 自身
...
priority_list_array[31] ←→ 自身 ← 优先级 31 的链表(空)2.3 就绪队列数据结构
2.3.1 readyq_bitmap 结构体
struct readyq_bitmap
{
#if OS_TASK_PRIORITY_MAX > 32
uint32_t priority_group_bmap; // 二级位图:组位图
uint8_t priority_bmap[(OS_TASK_PRIORITY_MAX + 7) / 8]; // 二级位图:组内位图
#else
uint32_t priority_bmap; // 一级位图(32 优先级)
#endif
os_list_node_t priority_list_array[OS_TASK_PRIORITY_MAX]; // 每个优先级一个链表头
};2.3.2 两种位图模式
| 模式 | 条件 | 位图结构 | 适用场景 |
|---|---|---|---|
| 一级位图 | OS_TASK_PRIORITY_MAX <= 32 | 单个 uint32_t | 大多数 MCU,包括 STM32F103 |
| 二级位图 | 32 < OS_TASK_PRIORITY_MAX <= 256 | priority_group_bmap + priority_bmap[] | 需要更多优先级的系统 |
STM32F103 使用一级位图模式,priority_bmap 是一个 32 位整数,bit 0 对应优先级 0,bit 31 对应优先级 31。
2.3.3 数据结构图示
gs_os_readyq
├── priority_bmap = 0b00000000_00000000_00000000_10000011
│ (bit 0、1、7 置位,表示优先级 0、1、7 有就绪任务)
│
└── priority_list_array[]
├── [0] → Task_A (prio 0) → Task_B (prio 0, 同优先级轮转)
├── [1] → Task_C (prio 1)
├── [2] → (空)
├── [3] → (空)
├── ...
├── [7] → Task_D (prio 7)
├── ...
└── [31] → (空)最高优先级任务 = os_ffs(0x83) - 1 = 0,即 Task_A。
三、就绪队列操作
3.1 位图操作函数
3.1.1 设置位图 — _k_readq_bmap_set
OS_INLINE void _k_readq_bmap_set(struct readyq_bitmap *readyq, uint8_t priority)
{
readyq->priority_bmap |= 1U << priority; // 将对应 bit 置 1
}示例:优先级 3 的任务就绪
priority_bmap 原值: 0b0000_0000_0000_0000_0000_0000_0000_0000
priority_bmap |= (1 << 3)
priority_bmap 新值: 0b0000_0000_0000_0000_0000_0000_0000_10003.1.2 清除位图 — _k_readq_bmap_clear
OS_INLINE void _k_readq_bmap_clear(struct readyq_bitmap *readyq, uint8_t priority)
{
readyq->priority_bmap &= ~(1U << priority); // 将对应 bit 清 0
}3.1.3 查找最高优先级 — _k_readyq_bmap_highest_task
OS_INLINE struct os_task *_k_readyq_bmap_highest_task(struct readyq_bitmap *readyq)
{
struct os_task *highest_task;
uint8_t highest_priority;
highest_task = OS_NULL;
if (readyq->priority_bmap != 0)
{
highest_priority = os_ffs(readyq->priority_bmap) - 1; // 找到第一个置位 bit
highest_task = os_list_entry(
readyq->priority_list_array[highest_priority].next,
os_task_t, task_node);
}
return highest_task;
}关键函数 os_ffs(Find First Set):查找 32 位整数中最低置位 bit 的位置(1-based),通常由编译器内建函数 __builtin_ffs 实现,单条 CPU 指令完成。
示例:
priority_bmap = 0b0000_0000_0000_0000_0000_0000_1000_0011
os_ffs(0x83) = 1 → bit 0 置位(优先级 0)
highest_priority = 0 → 取 priority_list_array[0] 的第一个任务3.2 就绪队列入队 — k_readyq_put
void k_readyq_put(struct os_task *task)
{
uint8_t priority;
priority = task->current_priority;
// 缓存最高优先级任务
if ((g_os_high_task == OS_NULL) || (priority < g_os_high_task->current_priority))
{
g_os_high_task = task;
}
// 设置位图
_k_readq_bmap_set(&gs_os_readyq, priority);
// 添加到对应优先级链表尾部(同优先级 FIFO)
os_list_add_tail(&gs_os_readyq.priority_list_array[priority], &task->task_node);
}入队流程:
k_readyq_put(Task_E, priority=3)
↓
更新 g_os_high_task(如果 Task_E 优先级更高)
↓
priority_bmap |= (1 << 3) ← 标记优先级 3 有任务
↓
os_list_add_tail(priority_list_array[3], &Task_E.task_node)
↓
Task_E 进入就绪队列3.3 就绪队列出队 — k_readyq_remove
void k_readyq_remove(struct os_task *task)
{
os_list_node_t *task_list_head;
uint8_t priority;
uint8_t highest_priority;
priority = task->current_priority;
task_list_head = &gs_os_readyq.priority_list_array[priority];
os_list_del(&task->task_node); // 从链表中删除
if (os_list_empty(task_list_head))
{
_k_readq_bmap_clear(&gs_os_readyq, priority); // 链表空,清除位图
if (task == g_os_high_task)
{
g_os_high_task = _k_readyq_bmap_highest_task(&gs_os_readyq); // 重新查找
}
}
else
{
if (task == g_os_high_task)
{
highest_priority = task->current_priority;
g_os_high_task = os_list_entry(
gs_os_readyq.priority_list_array[highest_priority].next,
os_task_t, task_node);
}
}
}出队流程:
k_readyq_remove(Task_E, priority=3)
↓
os_list_del(&Task_E.task_node) ← 从链表移除
↓
检查 priority_list_array[3] 是否为空
├── 空 → priority_bmap &= ~(1 << 3) ← 清除位图
│ 如果 Task_E 是 g_os_high_task → 重新查找
└── 非空 → 如果 Task_E 是 g_os_high_task → 取链表下一个四、调度流程概览
4.1 从初始化到首次调度
k_sched_init() ← 初始化就绪队列(本节)
↓
k_idle_task_init() ← 创建空闲任务(最低优先级)
↓
_k_sys_task_init() ← 创建 sys_task 系统任务
↓
os_kernel_start() → _k_run_init_call(PRE_KERNEL_2)
↓
k_start() ← 启动调度器
↓
g_os_next_task = g_os_high_task ← 选出最高优先级任务
↓
os_first_task_start() ← 汇编级切换到第一个任务4.2 调度触发时机
| 触发时机 | 说明 |
|---|---|
| 任务主动让出 CPU | os_task_yield() |
| 任务被阻塞 | os_task_tsleep()、os_sem_wait() 等 |
| 更高优先级任务就绪 | 中断或其它任务释放资源 |
| 时间片用完 | 同优先级任务轮转 |
| 中断退出 | OS_KERNEL_EXIT_SCHED() 检查是否需要调度 |
4.3 调度锁
int16_t g_os_sched_lock_cnt = 0;os_schedule_lock():g_os_sched_lock_cnt++,禁止调度os_schedule_unlock():g_os_sched_lock_cnt--,当减到 0 时触发调度- 用于保护临界区代码,防止被抢占
五、在启动流程中的位置
5.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() ← 3.5.4
↓
k_sched_init() ← 这里(3.5.5)
↓
k_timer_module_init() ← 定时器模块
↓
k_recycle_task_init() ← 回收任务
↓
k_idle_task_init() ← 空闲任务
↓
_k_sys_task_init() ← 系统任务5.2 为什么放在滴答队列之后?
- 调度器依赖滴答队列管理系统延时
- 滴答队列中唤醒的任务需要通过
k_readyq_put放入就绪队列 - 调度器本身不管理时间,只管"选谁运行"
- 两者配合:滴答队列管"何时唤醒",调度器管"唤醒后运行谁"
5.3 为什么放在任务创建之前?
- 空闲任务和系统任务创建时需要通过
k_readyq_put放入就绪队列 - 如果就绪队列未初始化,
k_readyq_put操作未定义的内存会崩溃 - 因此必须在任何任务创建之前完成就绪队列初始化
💡 本节总结
重点回顾
- 函数作用:初始化就绪队列位图,清空所有优先级链表
- 数据结构:
readyq_bitmap= 位图(uint32_t)+ 优先级链表数组(os_list_node_t[32]) - 查找算法:
os_ffs(priority_bmap)在 O(1) 时间内找到最高优先级就绪任务 - 同优先级轮转:同优先级任务按 FIFO 顺序执行(入队尾、出队头)
- 最高优先级缓存:
g_os_high_task避免每次os_ffs查找
关键符号说明
| 符号 | 来源 | 说明 |
|---|---|---|
gs_os_readyq | os_sched.c | 全局就绪队列位图 |
g_os_current_task | os_sched.c | 当前运行任务 |
g_os_next_task | os_sched.c | 下一个要运行的任务 |
g_os_high_task | os_sched.c | 最高优先级就绪任务缓存 |
g_os_sched_lock_cnt | os_sched.c | 调度锁计数器 |
priority_bmap | readyq_bitmap | 优先级位图(32 位) |
priority_list_array | readyq_bitmap | 每个优先级一个链表头 |
📚 扩展阅读
- 3.5.4 k_tickq_init 函数详解 - 滴答队列初始化
- 3.5.10 os_kernel_start 与 k_start 详解 - 调度器启动流程
下一步
接下来请学习:
- 3.5.6 k_timer_module_init 函数详解 - 定时器模块初始化