
B4课程优先级调度。B1 到 B3 主要是在把 OceanOS 跑起来能启动、能点灯、能打印 Hello world。到 B4开始碰调度里很实际的一个问题——好几个任务都就绪时CPU 给谁这就是OceanOS强大之处理硬件不再是”裸奔”。示例代码改动不大但跑起来以后现象会和前几课明显不一样。这个demo开始深入一点底层研究系统调度的优先级怎么执行的。示例代码在干什么图一main.c 里创建 main_task优先级是 1os_task_create(al_main_task, NULL, main_task,g_main_task_tcb, g_main_task_stack, AL_MAIN_TASK_STACK_SIZE, 1);al_main_task.c 里又创建了 task1优先级是 0os_task_create(al_task1, NULL, task1,g_task1_tcb, g_task1_stack, AL_TASK1_STACK_SIZE, 0);OceanOS 里数字越小优先级越高。0 是用户能设的最高档7 留给空闲任务。task1 死循环打印没有延时while (1){printf(task1 running...\n);}烧录后串口通常被 task1 running... 占满LED 几乎不闪。这不是硬件问题是调度结果。图二底层数据结构就绪表和TCBOceanOS 把所有准备好、等 CPU的任务放在全局就绪表 g_os_ready_tcb_table 里。它的结构在 os_type.hstruct os_tcb_table_t{os_int32u_t bit_map[OS_TCB_BIT_MAP_COUNT];struct{os_tcb_t *head;os_tcb_t *tail;} table[OS_TASK_PRIORITY_COUNT];};这里有两层第一层是按优先级分的8条链表。table[0] 到 table[7]每个下标对应一个优先级。同一优先级的任务串在同一条双向链表里head 指向队头tail 指向队尾。新来的任务加在尾部先来先服务。第二层是位图bit_map。哪个优先级上有任务对应 bit 就置 1该优先级全空bit 清 0。调度器不用遍历 8 条链表找有没有任务先看位图再定位具体优先级。每个任务的控制块 TCB 里task_priority 字段决定它进哪条链表os_int32u_t task_priority;//任务优先级os_int8u_t task_status;//运行态/就绪态/等待态os_tcb_t *prior_tcb;//同级链表前驱os_tcb_t *next_tcb;//同级链表后继另外还有 tick_sliceB4 示例里和优先级调度关系不大后面会提到。系统里还有两个全局指针g_os_tcb_current当前占着 CPU 的任务g_os_tcb_upcoming调度器选出来的下一个任务任务怎么进就绪表os_task_create() 创建任务时会把 TCB 初始化好然后调用 os_tcb_table_insert() 挂进就绪表。插入逻辑在 os_task.cos_int32u_t priority insert_tcb-task_priority;if (tcb_table-table[priority].tail NULL){//该优先级还没有任务head和tail都指向它insert_tcb-prior_tcb insert_tcb-next_tcb NULL;tcb_table-table[priority].head tcb_table-table[priority].tail insert_tcb;}else{//挂到链表尾部tcb_table-table[priority].tail-next_tcb insert_tcb;insert_tcb-prior_tcb tcb_table-table[priority].tail;insert_tcb-next_tcb NULL;tcb_table-table[priority].tail insert_tcb;}//位图对应bit置1tcb_table-bit_map[priority/32] | (1 (priority % 32));B4 启动后就绪表里大致是这样优先级任务0task11main_task7os_idle_task空闲任务os_start() 时自动创建task1 创建完成后如果 OS 已经在跑g_os_running 1os_task_create() 末尾会立刻调一次 os_task_schedule()。这就是抢占的入口——新任务优先级比当前任务高当前任务会被切下去。任务阻塞时比如 os_tick_delay()会从就绪表移除os_tcb_table_remove(g_os_ready_tcb_table, g_os_tcb_current);移除时维护双向链表如果该优先级最后一个任务也被拿走了位图对应 bit 清 0。延时到期后任务重新 insert 回就绪表再触发调度。结合B4示例走一遍上电后 main() 关中断调 os_init() 清空就绪表创建 main_task优先级 1进就绪表。os_start() 创建空闲任务优先级 7从就绪表选出最高优先级任务——此时是 main_taskPendSV 首次跳转进去。main_task 初始化串口、LED打印 Hello world创建 task1优先级 0。task1 进就绪表位图 bit 0 置 1立刻触发调度g_os_tcb_upcoming task1PendSV 把 main_task 的上下文存栈加载 task1 的栈跳进 task1 的死循环。此后只要 task1 就绪调度器每次搜索都命中优先级 0main_task 在优先级 1轮不到。task1 在优先级 0 链表里是独苗next_tcb NULL时间片关闭不会因为 SysTick 被换下去。main_task 唯一可能运行的时机task1 创建之前极短task1 阻塞或删除B4 代码里没有所以 LED 几乎不闪串口被刷屏——优先级调度和 PendSV 切换按设计在工作。空闲任务兜底os_start() 还会创建 os_idle_task优先级 OS_TASK_PRIORITY_COUNT - 1也就是 7。当所有用户任务都阻塞时就绪表里只剩空闲任务CPU 跑一个空 while(1)。B4 里几乎进不来因为 task1 永远就绪。B4 表面上只是改了个优先级数字底层其实串起来一整套机制就绪表按优先级分链、位图加速查找、os_task_schedule() 选出 g_os_tcb_upcoming、PendSV 完成栈和寄存器切换。把 task1 设成 0、main_task 设成 1再让 task1 死循环不阻塞等于人为制造了一个永远最高优先级、永远就绪的任务。看懂了为什么串口刷屏、LED 不闪优先级调度这块就算真正入门了。