这篇笔记关于:
sched_init造了进程 0move_to_user_mode开始跑进程 0
sched_init 本来是 [[2.main-init]] 的一部分,但因为和这里关系紧密,就提到这里来写了。
(懒得画图了,直接贴了上课手写的笔记,反正不会真的有人看吧。)
sched_init
// main
sched_init(); // kernel/sched.c
初始化调度程序以及「进程 0」:
task_union 与 task_struct:
用户栈与内核栈:
- 一个 proc 有自己的:一个 user stack,一个 kernel stack
- 正常用户态用自己的(不是别人的) user stack,入 k 则用自己的(不是公用的,甚至就没有共用的) kernel stack
- 不同 proc 用不同的 kernel stack:每个人做不同事、以不同目的入 k,都可以被打断,不能乱。
- 现在用的栈(压 main,压 sched_init)的是
user_stack- 目前 PL=0
- 之后
move_to_user_mode()就变了了 PL=3 的用户栈,给进程 0 在用户态用。
-
函数外顶层定义了进程槽:
struct task_struct * task[NR_TASKS] = {&(init_task.task), };-
第一项直接放了进程0:
INIT_TASK(进程0 的描述,硬编码写好了,在 include/linux/sched.h)
-
-
sched_init:
-
设进程0:
gdt[4]:TSS:任务状态描述符 -> 上下文gdt[5]:LDT:局部(段)描述符表 -> NULL、代码段、数据段- (both 104B)
-
其余进程置为空:
-
task 数组除了
task[0](init_task)全置为 NULL(共 63个 NULL) -
gdt从下标 6 开始:全部置为 0,预留给其他进程的 TSS 和 LDT
-
-
置
EFLAGS.NT = 0:精静止任务嵌套调用 -
ltr(0); lldt(0);- 赋 tr REG:任务 0 的 TSS 选择符
- 赋 ldtr REG:任务 0 的 LDT selector
- 0: 直接传任务号,宏自动算地址(include/liinux/sched.h)
-
设置时钟中断:
- 初始化 8253 定时器芯片:这东西以一定频率向 CPU 发时钟中断(0x20)
- 设 0x20 中断:
timer_interrupt(sustem_call.s)->do_timer(sched.c)- 当前 task 的
counter-- - 若
counter < 0,“你时间没了!”,调度
- 当前 task 的
-
设置
0x80中断:系统调用 -> system_call
-
move_to_user_mode
// main
move_to_user_mode();
Task 0 进入用户态(PL = 0 -> 3)
- 假造现场:模拟 INT 指令压栈:
- SS、ESP、EFLAGS、CS、EIP:(栈、状态、代码)
SS = 0x17 = 10111- 数据段、LDT、3 特权
- 即现在 PL 0 用的
user_stack
CS = 0x0f = 1111- 代码段、LDT、3 特权
iret:“返回”刚才造的假上下文(用户态)- PL = 0 -> 3
- SS = 0x10 -> 0x17(head.s 设的 0x10)
现在 task 0 就变成真正的进程了,现在也有了用户态、内核态的概念了,此后,就只有中断来才能从 PL 0 -> 3 了。
实际上新的 0x17 和以前的 0x10 指向同一个基址,只是长度、特权不同(访问控制施加于段):