“操作系统实验”实验报告

基于内核栈切换的进程切换

实验名称:基于内核栈切换的进程切换

实验日期:2022年5月24日

班级:软嵌201

姓名:胥千里

学号:2030110754

一.实验目的

1.深入理解进程和进程切换的概念;

2.综合应用进程、CPU 管理、PCB、LDT、内核栈、内核态等知识解决实际问题;

3.开始建立系统认识。

二.实验环境

硬件环境:IA-32(x86) 架构的 PC 机

软件环境:x86 模拟器 Bochs 、 GCC 编译器 、GDB 调试器 、Ubuntu (GNU/Linux)

三.实验内容

1.编写汇编程序 switch_to;

2.完成主体框架;

3在主体框架下依次完成 PCB 切换、内核栈切换、LDT 切换等;

4.修改 fork(),由于是基于内核栈的切换,所以进程需要创建出能完成内核栈切换的样子。

5.修改 PCB,即 task_struct 结构,增加相应的内容域,同时处理由于修改了 task_struct 所造成的影响。

6.用修改后的 Linux 0.11 仍然可以启动、可以正常使用。

7.(选做)分析实验 3 的日志体会修改前后系统运行的差别。

四.实验过程及数据记录

1、环境准备 cd /home/shiyanlou/oslab/ tar -zxvf hit-oslab-linux-20110823.tar.gz -C /home/shiyanlou/

图片描述

2、修改sched.c 将代码:

图片描述

3、修改switch_to:在kernel/system_call.s中添加以下switch_to汇编代码

图片描述

4、配合switch_to修改的补充 (1)开放switch_to 在system_call.s的头部附近添加以下代码

图片描述

5、改写task_struct 链接描述

图片描述

图片描述

6、ESPO和KERNEL_STACK及一些参数的设置

图片描述

7、修改fork()系统调用 (1)fork.c中添加代码

图片描述

8、(2)将first_return_kernel写在system_call.s头文件里

图片描述

图片描述

五.实验结果分析

由于 Linux 0.11 进程的内核栈和该进程的 PCB 在同一页内存上(一块 4KB 大小的内存),其中 PCB 位于这页内存的低地址,栈位于这页内存的高地址;另外,由于当前进程的 PCB 是用一个全局变量 current 指向的,所以只要告诉新 switch_to()函数一个指向目标进程 PCB 的指针就可以了。同时还要将 next 也传递进去,虽然 TSS(next)不再需要了,但是 LDT(next)仍然是需要的。

六.实验心得

虽然用一条指令就能完成任务切换,但这指令的执行时间却很长,这条 ljmp 指令在实现任务切换时大概需要 200 多个时钟周期。而通过堆栈实现任务切换可能要更快,而且采用堆栈的切换还可以使用指令流水的并行优化技术,同时又使得 CPU 的设计变得简单。所以无论是 Linux 还是 Windows,进程/线程的切换都没有使用 Intel 提供的这种 TSS 切换手段,而都是通过堆栈实现的。

对于汇编代码还不是很理解,比如mov指令:

七.思考题

回答下面三个题:

问题 1

针对下面的代码片段:

movl tss,%ecx

addl $4096,%ebx

movl %ebx,ESP0(%ecx)

copy

回答问题:

(1)为什么要加 4096;

因为页表是4KB

(2)为什么没有设置 tss 中的 ss0。

SS0、SS1和SS2分别是0、1和2特权级的栈段选择子。这里用不着特权级为0的内核段。

问题 2

针对代码片段:

*(--krnstack) = ebp;

*(--krnstack) = ecx;

*(--krnstack) = ebx;

*(--krnstack) = 0;

copy

回答问题:

(1)子进程第一次执行时,eax=?为什么要等于这个数?哪里的工作让 eax 等于这样一个数?

(2)这段代码中的 ebx 和 ecx 来自哪里,是什么含义,为什么要通过这些代码将其写到子进程的内核栈中?

(3)这段代码中的 ebp 来自哪里,是什么含义,为什么要做这样的设置?可以不设置吗?为什么?

答:(1)这里eax等于0,即返回值,这里设置就可以用if(!fork())来判断是不是子进程,最后一行(--krnstack) = 0完成了这件事。

(2)ebx和ecx都来自copy_process()函数传进来的参数,存放的是父进程的ebx和ecx,通过将父进程的参数压入栈中,就可以保证子进程回到用户态的时候具有和父进程相同的环境。

(3)ebp也是来自copy_process()函数传进来的参数,只是存放的是父进程的用户栈指针。即在fork()刚刚执行完copy_process()的时候,它的用户栈是父进程的用户栈,而非自己的用户栈,当子进程执行其他操作的时候,造成需要的栈将要与父进程不同了,才会创建自己的用户栈,这么做的好处是当一些子进程什么都不做时系统不用分配额外的空间,当然也可以创建子进程就为它分配一个新的栈,esp指向新的栈顶。

问题 3

为什么要在切换完 LDT 之后要重新设置 fs=0x17?而且为什么重设操作要出现在切换完 LDT 之后,出现在 LDT 之前又会怎么样?

cpu的段寄存器都存在两类值,一类是显式设置段描述符,另一类是隐式设置的段属性及段限长等值,这些值必须经由movl、lldt、lgdt等操作进行设置,而在设置了ldt后,要将fs显示设置一次才能保证段属性等值正确。

最新评论
暂无评论~