1.1 什么是进程?
进程是操作系统中的一个核心概念,它代表了计算机中程序的一次执行过程。在操作系统中,进程是资源分配和调度的基本单位,是程序的实体。进程不仅包含程序代码,还包括正在执行的活动,如程序计数器的值和处理器寄存器的内容。
1.2 进程的数据结构
context_t — 寄存器上下文
| 字段 | 类型 | 偏移 | 说明 |
|---|---|---|---|
esp |
uint32_t | +0 | 内核栈指针(pusha 帧顶 或 C 栈帧) |
ebp |
uint32_t | +4 | 用户栈顶(用户进程)/ 基址指针 |
ebx |
uint32_t | +8 | callee-saved |
esi |
uint32_t | +12 | callee-saved |
edi |
uint32_t | +16 | callee-saved |
process_t — 进程控制块(sizeof = 40)
| 字段 | 类型 | 偏移 | 说明 |
|---|---|---|---|
pid |
uint32_t | +0 | 进程 ID,0 = kernel_main |
ctx |
context_t | +4 | 寄存器上下文(20字节) |
state |
uint32_t | +24 | 进程状态(1 = 运行) |
started |
uint32_t | +28 | 是否已首次运行(0=未启动,1=已启动) |
kernel_stack |
uint32_t | +32 | 内核栈顶地址 |
sleep_ticks |
uint32_t | +36 | 剩余睡眠 tick 数,0 = 可调度 |
1.3 介绍
进程初始化时,首先初始化一个运行的、已启动的、pid为0的进程。随即创建一个内核态进程执行 task_kernel_init 函数。
MuxOS 目前采用时间片轮转调度,计时器每 tick 执行一次 irq0_stub。当执行 irq0_stub 时:
- 首先,会保存当前所有的寄存器数据(
pusha) - 随即向 PIC 发送 EOI
- 保存当前执行的进程索引后执行
process_tick
process_tick 首先会递减所有进程的 sleep_ticks,并获取第一个未休眠的进程索引。
当符合以下情况时,不会执行下一个进程(返回 -1):
- 所有进程都不可运行(全部在睡眠或只有 pid=0)
- 下一个可运行进程就是当前进程
进程切换逻辑
- 保存旧进程的
esp到processes[old].ctx.esp - 如果新进程
started=0,标记为started=1 - 切换
esp到新进程的内核栈 - 如果返回用户态(CS RPL=3),恢复用户段寄存器
popa恢复寄存器,iret返回到新进程
在 muxOS 中,提供了一些基础函数用来操作进程:
1 | void process_schedule(); // 主动调度函数(内核进程让出CPU时使用,非IRQ0路径) |
关于进程的介绍,请参考 加载进程 - OSDev 维基
关于本文
由 Latos 撰写,采用 CC BY-NC 4.0 许可协议。