ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从任务状态机到上下文切换:cavOS抢占式多任务调度手把手教程

从任务状态机到上下文切换:cavOS抢占式多任务调度手把手教程 从任务状态机到上下文切换cavOS抢占式多任务调度手把手教程【免费下载链接】cavOS Amd64 operating system in C. Trying to make a full OS, with a simple and readable codebase!项目地址: https://gitcode.com/gh_mirrors/ca/cavOScavOS 是一个用 C 语言编写的 amd64 操作系统以简单、可读的代码库著称。本文将带你完整理解它如何实现抢占式多任务调度从 12 种任务状态组成的状态机到时钟中断驱动的上下文切换每一步都有对应源码可查。想动手跟读源码先克隆仓库git clone https://gitcode.com/gh_mirrors/ca/cavOS为什么内核需要任务状态机多任务系统的核心难题是CPU 同一时刻只能跑一个任务内核必须知道谁该跑、谁在等、谁已死。cavOS 的答案非常直观——每个任务Task身上都有一个state字段全部状态定义在 src/kernel/include/task.h状态含义TASK_STATE_CREATED刚由taskCreate()创建尚未就绪TASK_STATE_READY就绪排队等待 CPUTASK_STATE_IDLE空闲TASK_STATE_WAITING_INPUT等待 I/O 输入如读键盘TASK_STATE_WAITING_CHILD等待子进程退出wait4TASK_STATE_WAITING_VFORK等待 vfork 子进程TASK_STATE_BLOCKED被阻塞TASK_STATE_FUTEX在 futex 上睡眠TASK_STATE_SIGKILLED被信号杀死TASK_STATE_DEAD已死亡等待回收这套状态机没有复杂的状态转换函数而是由调度器、系统调用和唤醒路径直接读写state字段。正因为代码极简你只需盯着 src/kernel/multitasking/task.c 里taskCreateFinish()一句话——task-state TASK_STATE_READY;——就能看清新任务就绪的完整语义。抢占的秘密时钟滴答如何驱动调度器抢占式调度的触发点是硬件定时器。在 src/kernel/cpu/timer.c 中逻辑只有三行void timerTick(uint64_t rsp) { timerTicks; schedule(rsp); }每次 APIC 定时器中断内核递增全局timerTicks毫秒级时间基准然后无条件调用调度器schedule(rsp)。也就是说无论当前任务是否愿意每隔一个滴答CPU 归属权就要被重新裁定一次——这就是抢占。 定时器初始化见 src/kernel/cpu/timer.c先用 PIT 校准再配置 APIC 周期中断。逐行读懂调度函数 schedule()整个抢占式调度器只有一个约 150 行的函数 src/kernel/multitasking/schedule.c。它的执行流程可以拆成 5 步第 1 步在环形任务链上找下一个就绪任务任务以双向链表串成环调度器从currentTask-next开始顺藤摸瓜碰到READY任务 → 选中它碰到有挂起信号且可唤醒的任务 → 强制置为READY信号抢占见 schedule.c#L32-L40碰到已到强制唤醒时间forcefulWakeupTimeUnsafe的任务 → 唤醒圈扫两圈找不到任何人 → 交给dummyTask兜底防止死锁空转。第 2 步处理旧任务与新任务的交接杂务若旧任务正持有自旋锁队列位置则释放schedule.c#L67-L71为新任务检查setitimer实时定时器到期则投递SIGALRMschedule.c#L73-L85处理新任务的挂起信号若途中被 SIGKILL 则回退重选schedule.c#L96-L103。第 3 步交换运行环境这是上下文切换的精华全部在 schedule.c#L105-L136栈指针把新任务的栈顶写进 TSS 的rsp0这是陷入内核时 CPU 自动切换用户栈的依据寄存器memcpy把当前 CPU 的全部寄存器快照存入旧任务old-registers新任务的寄存器稍后直接种到栈上省去一次写回FPU/SSE用fxsave/fxrstor保存恢复 512 字节的浮点环境避免浮点状态串味地址空间通过ChangePageDirectoryFake 汇编收尾切换新任务的页目录实现进程级内存隔离。每个任务要保存什么看Task结构体一目了然寄存器快照、TSS 栈、页目录、fsbase/gsbaseTLS、FPU 环境全在 src/kernel/include/task.h 里。第 4 步iretq 完成最终归还schedule()最后把新任务的寄存器栈帧压到它的内核栈上然后跳转汇编函数asm_finalize——它负责切页表、iretq回到新任务schedule.c#L142-L156。iretq弹出新任务的 RFLAGS/RIP/CS任务仿佛从未被中断般继续执行。至此一次完整的抢占式上下文切换结束。自愿让出 CPUhandControl() 的巧妙设计抢占之外任务还可以自觉让出 CPU。src/kernel/cpu/system.c 中的handControl()做法堪称极简美学currentTask-schedPageFault true; volatile uint8_t _drop *(uint8_t *)(SCHED_PAGE_FAULT_MAGIC_ADDRESS);它故意解引用一个魔法地址触发缺页异常内核识别出这是自找的缺页后转入调度器。sleep()timer.c#L36-L42、futex 睡眠、轮询 I/O 等都靠它让出 CPU——用一条指令换来了可被抢占的自愿调度点。任务从哪来taskCreate() 与 taskFork()调度的原料由任务管理代码生产入口在 src/kernel/multitasking/task.ctaskCreate()分配Task结构、设置TASK_STATE_CREATED为新 ELF 进程摆好入口rip和参数taskCreateFinish()初始化完成后一步置为READY正式入队taskFork()克隆现有任务的寄存器与资源支撑fork()/clone()系统调用任务死亡时置TASK_STATE_DEAD并由回收路径清理task.c#L249。配合 src/kernel/syscalls/linux/syscalls_proc.c 中的fork、execve、wait4等 Linux 兼容系统调用普通 Linux 二进制得以直接在 cavOS 上运行。学习路线图按这个顺序读源码步骤文件看什么1src/kernel/include/task.h状态机与Task结构体全貌2src/kernel/cpu/timer.c时钟中断如何进入schedule()3src/kernel/multitasking/schedule.c选任务 → 换环境 → iretq 的完整流程4src/kernel/cpu/system.chandControl()自愿让出的实现5src/kernel/multitasking/task.c任务创建、fork、死亡与回收6src/kernel/multitasking/task_info.c文件系统、页目录、文件描述表等共享资源小结cavOS 的多任务调度是小而全的典范状态机12 种状态覆盖就绪、阻塞、等待子进程、futex、信号杀死等全部场景抢占APIC 时钟中断 →timerTick()→schedule()每滴答重裁定 CPU 归属切换一次memcpy存寄存器 fxsave/fxrstor存浮点 换页目录 iretq归还控制150 行内讲清全部细节协作handControl()用自触发缺页实现自愿让出优雅且零额外机制。如果你想在几小时内看懂一个操作系统的调度器cavOS 的 src/kernel/multitasking/ 目录就是最好的教材——没有历史包袱每一行都值得读。【免费下载链接】cavOS Amd64 operating system in C. Trying to make a full OS, with a simple and readable codebase!项目地址: https://gitcode.com/gh_mirrors/ca/cavOS创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表