Linux 0.11 main: 神说要有进程 0

1,309 阅读2分钟

这篇笔记关于:

  • sched_init 造了进程 0
  • move_to_user_mode 开始跑进程 0

sched_init 本来是 [[2.main-init]] 的一部分,但因为和这里关系紧密,就提到这里来写了。

(懒得画图了,直接贴了上课手写的笔记,反正不会真的有人看吧。)

sched_init

// main
sched_init();  // kernel/sched.c

初始化调度程序以及「进程 0」:


task_uniontask_struct:

task_union.png

用户栈与内核栈:

  • 一个 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)

      INIT_TASK.png

  • sched_init:

    • 设进程0:

      • gdt[4]:TSS:任务状态描述符 -> 上下文
      • gdt[5]:LDT:局部(段)描述符表 -> NULL、代码段、数据段
      • (both 104B)
    • 其余进程置为空:

      sched_init.png

      • 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,“你时间没了!”,调度
    • 设置 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 指向同一个基址,只是长度、特权不同(访问控制施加于段):

image-20221016191030697.png