Skip to content

多任务

是操作系统实现的必要功能, 多任务部分负责创建/销毁/调度进程or线程

无论是宏内核还是微内核, 其内核都必须具备多任务调度的机制

系统进程

是运行在R0 特权级的进程, 一般这类进程为IDLE进程, 系统服务进程, 拥有PC的最高操作权限

IDLE 进程
是操作系统第一个创建的进程, 唯一一个不通过fork()函数复制得来的进程, 其PID为0
该进程的调度优先级为最低, 一般在调度队列为空时调度该进程
操作系统的初始化等工作由该进程完成(在多核中由CPU0的IDLE进程完成,且有几个CPU核心就有几个IDLE进程)
在操作系统内核初始化完成后该进程并不会销毁,而是循环执行hlt以降低CPU功耗

用户进程

运行在 R3 特权级的进程, 一般这类进程为应用程序的进程, 只能访问特定内存区域且无法执行特权级指令(如hlt``sti lgdt等)

  • 在x86平台下, CPU无法从R3特权级直接切换到R0, 同时R0也无法直接切换至R3, 所以在x86中唯一从R0切换至R3的方式是使用 iret指令跳转
  • 涉及特权级切换时, 内核必须配置TSS(Task Statu Segments : 任务状态段), 该结构是GDT的一个段描述符
  • 每个用户进程会在内核有一块单独的栈, 该栈用于用户进程系统调用时候存储R0特权级执行时产生的各种数据

在CoolPotOS中, 用户进程在刚创建时是R0特权级, 这时候内核会往该进程的栈中插入一个名为switch_to_user_mode 的函数并传入真正的用户程序入口地址
switch_to_user_mode函数负责初始化进入R3前各种寄存器的值
最后使用iret指令跳转到用户程序入口地址, 这个时候就切换至R3特权级了

TSS

任务状态段, 负责存储一个任务的当前CPU状态, 其包含了CPU所有的寄存器(控制寄存器以及GDTR IDTR除外). 是旧版x86平台用于任务切换的一个数据结构。

在现代操作系统中并不会使用该数据结构去进行任务调度, 因为PCB(Process Control Block : 进程控制块)取代了它。 PCB相比TSS拥有更快的任务切换速度, 且可以自定义多种进程私有的数据结构(如PID TTY设备映射 调度时间片计数等)。

但这并不代表不需要配置TSS, TSS的作用只是缩减到切换内核栈地址的作用

任务调度

任务调度有很多方式(抢占式调度/顺序调度), 内核会使用合理的方式分配给这些任务一定的CPU时间片, 一旦过了该任务的CPU时间片就会触发该任务调度

在用户程序发生系统调用时禁止进行该CPU的任务切换, 不然syscall无法正确返回

进程切换过程中, 需要切换的寄存器可以参考TSS的结构, 以下展示了进程如何切换上下文

c
struct context{
    uint32_t esp;
    uint32_t ebp;
    uint32_t ebx;
    uint32_t esi;
    uint32_t edi;
    uint32_t ecx;
    uint32_t edx;
    uint32_t eflags;

    uint32_t eax;
    uint32_t eip;
    uint32_t ds;
    uint32_t cs;
    uint32_t ss;
    fpu_regs_t fpu_regs; // FPU 标记, 是否启用FPU
};

补充

  • 进程间的资源隔离
    因为不同的进程之间页目录不同, 拥有不同的内存布局, 不同进程间相同的线性地址指向的是不同的物理地址
    因此该地址获取的数据也不同

  • 线程与进程的区别
    线程在上下文切换的寄存器要比进程少(只会切换esp ebp eax ebx等寄存器),
    所以线程之间的资源可以共享。

  • 有关于更多的syscall系统调用的信息, 请参考用户程序部分。