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

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

实验名称:基于内核栈切换的进程切换
实验日期:5月24日
班级:软嵌191
姓名:游金明
学号:1930110795
一、实验目的

  1. 有序列表 深入理解进程和进程切换的概念;
  1. 有序列表 综合应用进程、CPU 管理、PCB、LDT、内核栈、内核态等知识解决实际问题;
  2. 有序列表 开始建立系统认识 二、实验环境
    本操作系统实验的硬件环境是 IA-32(x86) 架构的 PC 机(在实验楼的环境中就是右侧的窗口),主要软件环境是 Bochs + gcc + 你最喜欢的编辑器 / IDE + 你最喜欢的操作系统 + Linux 0.11 源代码。

三、实验内容

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

四、实验过程及数据记录

  1. 实验环境
    $ cd /home/shiyanlou/oslab/

$ tar -zxvf hit-oslab-linux-20110823.tar.gz
-C /home/shiyanlou/ 切换目录 $ cd /home/shiyanlou/oslab/

确认路径 $ pwd

查看目录内容 $ ls -l

图片描述 2.修改sched.c

if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
    c = (*p)->counter, next = i;

//......

switch_to(next);
copy

修改为

if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
    c = (*p)->counter, next = i, pnext = *p;

//.......

switch_to(pnext, LDT(next));
copy

图片描述 3.删除sched.h中switch_to图片描述 4.在system_call.s重写switch_to 图片描述

重写first_return_kernel

first_return_kernel:
 popl %edx
 popl %edi
 popl %esi
 pop %gs
 pop %fs
 pop %es
 pop %ds
 iret
copy

图片描述 设置全局可见

.globl switch_to
.globl first_return_from_kernel
copy

图片描述 5.修改fork()系统调用 图片描述 6.forck.c修改 -注释基于tss进程切换机制时的代码,

p->tss.back_link = 0;
    p->tss.esp0 = PAGE_SIZE + (long) p;
    p->tss.ss0 = 0x10;
copy

krnstack指针指向子进程的内核栈

long * krnstack = (long *)(PAGE_SIZE+(long)p);
copy

初始化内核栈(krnstack)中的内容:

*(--krnstack) = ss & 0xffff;
    *(--krnstack) = esp;
    *(--krnstack) = eflags;
    *(--krnstack) = cs & 0xffff;
    *(--krnstack) = eip;

    *(--krnstack) = (long) first_return_kernel;//处理switch_to返回的位置

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

图片描述 添加extern声明

extern long switch_to;
extern long first_return_from_kernel;
copy

图片描述 五、实验结果分析 -编译 图片描述 -测试

图片描述

switch_to (在 kernal/system_call.s 中)实现去掉,写成一段基于堆栈切换的代码。

六、实验心得
-问题1
(1)这里的ebx寄存器指向的是下一个进程的PCB,加上4096后,即为一个进程分配4KB(4*1024)的空间,栈顶即为内核栈的指针,栈底即为进程的PCB。
(2)因为进程的切换不依靠tss,但CPU的机制造成对每个进程仍然会有TR寄存器、tss的设置等内容,所以可以让所有的进程都共用tss0的空间,所以不需要设置tss0。
-问题2
(1)这里eax等于0,即返回值,这里设置就可以用if(!fork())来判断是不是子进程,最后一行(--krnstack) = 0完成了这件事。
(2)ebx和ecx都来自copy_process()函数传进来的参数,存放的是父进程的ebx和ecx,通过将父进程的参数压入栈中,就可以保证子进程回到用户态的时候具有和父进程相同的环境。
(3)ebp也是来自copy_process()函数传进来的参数,只是存放的是父进程的用户栈指针。即在fork()刚刚执行完copy_process()的时候,它的用户栈是父进程的用户栈,而非自己的用户栈,当子进程执行其他操作的时候,造成需要的栈将要与父进程不同了,才会创建自己的用户栈,这么做的好处是当一些子进程什么都不做时系统不用分配额外的空间,当然也可以创建子进程就为它分配一个新的栈,esp指向新的栈顶。
-问题3
首先要知道FS的作用: 操作系统就是通过FS在内核态中找到用户程序的地址的,切换LDT的时候,也就是会切换进程使用的用户栈,所以用户地址已经改变了,自然FS也要跟着改变,如果出现在切换LDT之前就可能会影响其他进程查找对应的用户地址。

本次实践项目就是将 Linux 0.11 中采用的 TSS 切换部分去掉,取而代之的是基于堆栈的切换程序。具体的说,就是将 Linux 0.11 中的 switch_to 实现去掉,写成一段基于堆栈切换的代码。

最新评论
暂无评论~