操作系统 多进程图像 实验五-基于内核栈切换的进程切换
实验五 基于内核栈切换的进程切换
本文资源来自 哈工大李治军老师的 操作系统原理与实践 课程
实验地址:https://www.lanqiao.cn/courses/115
课程地址:https://www.bilibili.com/video/BV1d4411v7u7
部分图文来自 《操作系统真像还原》 郑刚,带我走进操作系统
前言
对初学者的建议
如果想真正学会操作系统,需要的远不仅仅是“看懂”CPU 内存管理 外设 IO 的 抽象原理,真正要做的是动手去写。而最易懂的方法是 在linux中操作。通过 bochs 自动生成img镜像 自己往镜像里面写一个不那么完整操作系统,并在此过程中调试,最终逐渐理解一切
~~
想要做实验,需要具有以下基础
- intel汇编,AT&T汇编
- 会拆解内联汇编
- c语言,尤其指针和结构体部分
- linux命令
上图展示了 linux 0.11版本 从系统加电起执行程序的顺序,其中 .s是汇编源代码文件,.c是c|c++文件。因此需要系统学习汇编和c语言基础,我们在内核部分还会碰到在c语言内嵌入汇编代码的情况,因此这两部分是硬实力,必须掌握。除此之外推荐“拿来主义”,到了再学。
TSS PCB/TCB LDT
TSS
TSS ,即 Task State Segment ,意为任务状态段,它是处理器在硬件上原生支持多任务的一种实现方式,下图是它的数据结构,TSS 是每个任务都有的结构 它用于任务的标识,相当于任务的身份证,程序拥有此结构才能运
行,这是处理器硬件上用于任务管理的系统结构。通过在GDT注册后使用
从28-100就是把寄存器压栈,存储该任务的状态,espX代表了对应特权级的栈地址。本实验中因为tss用于操作内核栈,故使用esp0。
PCB
操作系统为每个进程提供了 PCB ,Process Control Block,即程序控制块,它就是进程的身份证,用它来记录与此进程相关的信息,比如进程状态、 PID 、优先级等。又可称为进程表项。tcb就是thread CB,类比。
要注意的是,PCB格式并不唯一,大小以 页(4KB)为单位,而本实验中的内核栈指针位于满栈的栈顶。
LDT
LDT Local Descriptor Table 的缩写,即局部描述符表。Inter建议为每个程序单独赋予一个程序描述其私有资源(代码段,数据段,栈段)的数据结构,即LDT。在GDT注册使用
通过选择子找到对应的LDT描述符,再通过段基址:偏移+映射找到对应私有资源地址。
实验逻辑
本实验最终要求不完全依靠TSS,用PCB和LDT实现堆栈形式的基于内核栈切换的进程切换。
其中内核级进程使用的是fork()陷入,因此简易了创建新进程的步骤。
进程切换最关键的函数在 switch_to,通过TCB的内核栈指针,ret到内核程序,用CS:IP切到用户段。
步骤
修改schedule调度函数
1 |
|
- pnext——下一进程的指针,与current对应。
- LDT(next)——下一进程的LDT地址
- current——在 <asm/current.h> 中定义, 它产生一个指针指向结构 task_struct,在此指向当前运行进程
next在没学选择子前,可以理解为GDT数组标号。
补充 c语言函数调用栈
在schedule()中,switch是被调函数,所以ebp位置如图(EBP of callee function)
所以在schedule的函数调用栈中,
switch_to运行时,2个参数分别位于8(ebp)和12(ebp)。
实现不主要依靠TSS的switch_to()
1 |
|
1 |
|
修改fork.c
fork直译是叉子,可以形象理解为最上面的尖头是父进程,下面的是子进程,是通过拷贝父进程+修改生成的
这是fork.c修改的部分代码,是看懂switch必须会的部分。
1 |
|
思考问题
针对下面的代码片段:
1 |
|
回答问题:
- (1)为什么要加4096;
在copy_process()中,子进程的PCB大小设置为一页4KB,而内核栈基址设在了页末所以是 PCB指针+4096
- (2)为什么没有设置tss中的ss0。
对于linux0.11来说,内核栈段的选择子永远都是0x10,故ss0不用初始化
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!