载入实验环境
首先配置环境
cd /home/shiyanlou/oslab
tar -zxvf hit-oslab-linux-20110823.tar.gz -C /home/shiyanlou
实验过程截图
分别对/kernel/fork.c
、kernel/system_call.s
、/kernel/sched.c
、/include/linux/sched.h
进行修改,截图如下:
注释掉原来switch_to
宏函数,将新的switch_to()
函数作为一个系统调用函数重写在汇编文件system_call.s
中:
让所有进程共用一个TSS(这里使用0号进程的TSS)需要定义一个全局指针变量tss(放在system_call.s中)来执行0号进程的TSS:
在内核栈指针重写指令中有宏定义ESP0
,所以在上面需要提前定义好 ESP0 = 4
,(是因为TSS中内核栈指针ESP0
放在偏移为4的地方)
修改以后的fork()
要使得父子进程共享同一块内存空间、堆栈和数据代码块。
首先需要将first_return_kernel
设置在全局可见,然后再在fork.c
中添加该函数声明:
在sched.h
中的task_struct
(也就是pcb
)中添加kernelstack
。
最后,将first_return_kernel
(属于系统调用,而且是一段汇编代码)写在system_call.s
头文件里,回到~/oslab
目录下:
make all
cd linux-0.11
../run
实验结果截图
问题回答
问题 1
针对下面的代码片段:
movl tss,%ecx
addl $4096,%ebx
movl %ebx,ESP0(%ecx)
回答问题:
- (1)为什么要加 4096;
- (2)为什么没有设置 tss 中的 ss0。
回答1
**(1)**由于Linux 0.11进程的内核栈和该进程的PCB在同一页内存上,而此内存大小为4KB(4*1024)。PCB位于这页内存的低地址,栈位于这页内存的高地址,如果要得到内核栈地址就需要加4096。
**(2)**tss.ss0是内核数据段,进程的切换不依靠tss,但CPU的机制让所有寄存器都会有tss字段。所以可以让所有的进程都共用tss0的空间,不需要设置tss0。
问题 2
针对代码片段:
*(--krnstack) = ebp;
*(--krnstack) = ecx;
*(--krnstack) = ebx;
*(--krnstack) = 0;
回答问题:
- (1)子进程第一次执行时,eax=?为什么要等于这个数?哪里的工作让 eax 等于这样一个数?
- (2)这段代码中的 ebx 和 ecx 来自哪里,是什么含义,为什么要通过这些代码将其写到子进程的内核栈中?
- (3)这段代码中的 ebp 来自哪里,是什么含义,为什么要做这样的设置?可以不设置吗?为什么?
回答2
**(1)**eax=0,即返回值。以便用if(!fork())来判断是不是子进程。最后一行(–krnstack) = 0完成了这件事。
**(2)**ebx和ecx来自copy_process()的形参,是段寄存器。为了让父子的内核栈在回到用户态初始化时完全一致。
**(3)**ebp也是来自copy_process()函数传进来的参数,存放的是父进程的用户栈指针。即在fork()刚刚执行完copy_process()的时候,它的用户栈是父进程的用户栈;当子进程执行其他操作的时候,需要的栈与父进程不同时,才会创建自己的用户栈。这样系统不用分配额外的空间给闲置子进程。不可以不设置,因为它存放了用户栈,虽然也可以创建子进程为它分配一个新的栈,esp指向新的栈顶,但是对于父进程是不可删除的。