操作系统(四)基于内核栈切换的进程切换

Jan 于 2024-09-10 发布 浏览量

实验内容

编写汇编程序 switch_to:
完成主体框架;
在主体框架下依次完成 PCB 切换、内核栈切换、LDT 切换等;
修改 fork(),由于是基于内核栈的切换,所以进程需要创建出能完成内核栈切换的样子。
修改 PCB,即 task_struct 结构,增加相应的内容域,同时处理由于修改了 task_struct 所造成的影响。
用修改后的 Linux 0.11 仍然可以启动、可以正常使用。
(选做)分析实验 3 的日志体会修改前后系统运行的差别。

回答下面三个问题:

问题 1 针对下面的代码片段:

movl tss,%ecx
addl $4096,%ebx
movl %ebx,ESP0(%ecx)

回答问题:

(1)为什么要加 4096; (2)为什么没有设置 tss 中的 ss0。

问题 2 针对代码片段:

*(--krnstack) = ebp;
*(--krnstack) = ecx;
*(--krnstack) = ebx;
*(--krnstack) = 0;

回答问题:

(1)子进程第一次执行时,eax=?为什么要等于这个数?哪里的工作让 eax 等于这样一个数?
(2)这段代码中的 ebx 和 ecx 来自哪里,是什么含义,为什么要通过这些代码将其写到子进程的内核栈中?
(3)这段代码中的 ebp 来自哪里,是什么含义,为什么要做这样的设置?可以不设置吗?为什么?

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

实验内容

修改以下文件

kernel/sched.c

kernel/system_call.s

kernel/fork.c

include/linux/sched.h

二、回答问题 问题一:针对下面的代码片段:

movl tss,%ecx
addl $4096,%ebx
movl %ebx,ESP0(%ecx)

回答问题:

问题二:针对下面代码片段

*(--krnstack) = ebp;
*(--krnstack) = ecx;
*(--krnstack) = ebx;
*(--krnstack) = 0;

回答问题:

答:eax = 0;还记得我们改写的fork.c吗?其中一句代码*(–krnstack) = 0;其实就是将内核栈用用于返回给eax寄存器的内容置为0,最后eax的内容会返回给fork函数

答:ebx和ecx来自copy_process()的形参,形参的来源是各个段寄存器。对于fork函数而言,子进程是父进程的拷贝,就是要让父子进程共用同一个代码、数据和堆栈。

答:ebp是用户栈地址,一定要设置,不设置子进程就没有用户栈了

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

答:这两句代码的含义是重新取一下段寄存器fs的值,这两句话必须要加,也必须要出现在切换完LDT之后,这是因为通过fs访问进程的用户态内存,LDT切换完成就意味着切换了分配给进程的用户态内存地址空间,所以前一个fs指向的是上一个进程的用户态内存,而现在需要执行下一个进程的用户态内存,所以就需要用这两条指令来重取fs。 出现在LDT之前访问的就还是上一个进程的用户态内存。

修改文件如下

zip

作于2024年09月10日