操作系统lab3实验总结

一、进程相关函数

在做lab3的实验时,发现函数嵌套的情况很多,首先整理这一块的逻辑。

箭头表示函数调用

1.初始化:
  • 申请envs[]的空间;初始化env_free_list(把空闲进程env_status设置为ENV_FREE)
2.创立进程:设置env_pri
  • env_alloc():
    • 从env_free_list取出一块空闲进程;
    • 设置env_id,env_status,env_parent_id,env_tf.cp0_status,env_tf.regs[29]
    • env_setup_vm():
      • 为进程创建一页页目录,并建立好自映射
      • 设置env_pgdir,env_cr3
  • load_icode():
    • 为进程申请一页作为栈,并建立好映射
    • 设置env_tf.pc(为load_elf返回的binary的入口)
    • load_elf()/load_icode_mapper():

      • 以一个段(segment)为单位,把binary(进程的内容的二进制镜像)的内容复制到所在内存的虚拟地址

      • (load_elf负责找入口,mapper负责copy)

3.切换进程
  • 保存当前进程的上下文,设置env_tf,env_tf.pc
  • 恢复要启动的进程上下文,并启动新进程,设置env_status,env_pgdir,操作了env_tf,env_id

二、进程控制块(PCB) 

进程控制块(PCB) 是系统为了管理进程设置的一个专门的数据结构,用它来记录进程的外部特征,描述进程的运动变化过程。系统利用PCB 来控制和管理进程,所以 PCB 是系统感知进程存在的唯一标志 

首先贴出PCB的构成。

 1 struct Env {
 2 /* Trapframe 结构体的定义在include/trap.h 中,
 3  * env_tf 的作用就是在进程因为时间片用光不再运行时,
 4  * 将其当时的进程上下文环境保存在env_tf 变量中。
 5  * 当从用户模式切换到内核模式时,内核也会保存进程上下文,
 6  * 因此进程返回时上下文可以从中恢复。
 7  */
 8 struct Trapframe env_tf; // Saved registers
 9 
10 LIST_ENTRY(Env) env_link; // Free LIST_ENTRY 构造空闲进程链表。
11 
12 u_int env_id; // Unique environment identifier
13 
14 /*该变量存储了创建本进程的进程id。
15  *这样进程之间通过父子进程之间的关联可以形成一颗进程树。
16  */
17 u_int env_parent_id; // env_id of this env's parent
18 /*env_status : 该变量只能在以下三个值中进行取值:
19   – ENV_FREE : 表明该进程是不活动的,即该进程控制块处于进程空闲链表中。
20 
21   – ENV_NOT_RUNNABLE : 表明该进程处于阻塞状态,处于该状态的进程往往在
22     等待一定的条件才可以变为就绪状态从而被CPU 调度。
23 
24   – ENV_RUNNABLE : 表明该进程处于就绪状态,正在等待被调度,但处于RUNNABLE 
25     状态的进程可以是正在运行的,也可能不在运行中。*/
26 u_int env_status; // Status of the environment
27 
28 Pde *env_pgdir; // Kernel virtual address of page dir 这个变量保存了该进程页目录的虚拟地址。
29 
30 u_int env_cr3;// 这个变量保存了该进程页目录的物理地址。
31 
32 LIST_ENTRY(Env) env_sched_link;//这个变量来构造就绪状态进程链表。
33 
34 u_int env_pri;//这个变量保存了该进程的优先级。
35 
36 };
PCB

 1.env_tf

env_tf的类型是struct Trapframe,定义在trap.h中。

 1 struct Trapframe { //lr:need to be modified(reference to linux pt_regs) TODO
 2         /* Saved main processor registers. */
 3         unsigned long regs[32];
 4 
 5         /* Saved special registers. */
 6         unsigned long cp0_status;
 7         unsigned long hi;
 8         unsigned long lo;
 9         unsigned long cp0_badvaddr;
10         unsigned long cp0_cause;
11         unsigned long cp0_epc;
12         unsigned long pc;
13 };
Trapframe

regs[29]:通用寄存器中的29号是栈寄存器,在env_alloc()的时候设置为USTACKTOP(是用户栈,内核栈在0x8040 0000)

我们回忆起在load_icode()也申请了一页作为栈映射到了USTACKTOP-BY2PG。刚好是regs[29]所在起始位置的下一页。

所以在env_alloc()的时候调整了栈指针的位置为USTACKTOP,在env_icode()时为栈专门申请了一页的空间[USTACKTOP-BY2PG,USTACKTOP]。

pc:程序计数器,用于存放下一条指令的地址

上面的函数我们一共有两个地方用到了pc。

第一处在env_alloc()

e->env_tf.pc = entry_point;
即将进程的起始地址移动到了binary的e_entry,可执行程序入口点地址。
第二处在env_run()
curenv->env_tf.pc = curenv->env_tf.cp0_epc;
env_tf.cp0_epc存的是下一条指令的地址,则将下一个pc的地址保存了,回复这个进程的时候可以直接跳转到那个位置。
cp0_status
在env_alloc()中进行了这样的设置:
e->env_tf.cp0_status = 0x10001004;
指导书中提到“ MIPSR3000 里的SR(status register) 寄存器就是我们在env_tf里的cp0_ status,R3000 的SR 寄存器的低六位是一个二重栈的结构。”
二重栈在这个地方应该是指以大小2为单位的栈。所以实际上在中断发生和中断恢复时,会经历这样的倒腾。

KUo 和IEo 是一组,每当中断发生的时候,硬件自动会将KUp 和IEp 的数值拷贝到这里;KUp 和IEp 是一组,当中断发生的时候,硬件会把KUc 和IEc 的数值拷贝到这里。其中KU 表示是否位于内核模式下,为1 表示位于内核模式下;IE 表示中断是否开启,为1 表示开启,否则不开启2。

而每当rfe 指令调用的时候,就会进行上面操作的逆操作。---《指导书》

我后来发现PPT里有,请跳过这部分。
这一段没有关于KUo、IEo、KUp、IEp、KUc、IEc的解释,我估计是这样的,画图说明。

下面这一段代码在运行第一个进程前是一定要执行的,所以就一定会执行rfe这条指令。

lw k0,TF_STATUS(k0) # 恢复CP0_STATUS 寄存器

mtc0 k0,CP0_STATUS

j k1

rfe

KU:1--内核态,0--用户态;IE:1--开启中断,0--关闭中断。(这里应该是看KUc,IEc)
rfe会发生类似于中断恢复的操作,往右移动。我们为了设置初始状态为000001b(进入用户态,开启中断),所以我们先设置为000100b,再触发ref指令,使之变成000001b。
我们之前的设置是e->env_tf.cp0_status = 0x10001004;其中还设置了“第28bit 设置为1,表示处于用户模式下。第12bit 设置为1,表示4 号中断可以被响应。”

2.lcontext(curenv->env_pgdir);

curenv->env_pgdir是页目录的内核虚拟地址
lontext 中有一句指令sw a0,mCONTEXT (a0是第一个参数即新进程的内核虚拟地址)

在pmap.c中有这样一句mCONTEXT = (int)pgdir;是把全新的页目录kva存到mCONTEXT
这里就是把curenv的页目录kva存到mCONTEXT,mCONTEXT除了第一次创建页目录的使用,还会在do_refill里使用,这里暂不了解。这里大概是为了开启一个全新的进程时需要创建进程的页目录,需要mCONTEXT。

三、TIMESTACK

这一部分不一定正确,欢迎指正!

在mmu.h中定义了TIMESTACK是0x82000000,在dump中定义了KERNEL_SP是0x80017500(不是很确定,这个位置在Kernel Stack区域)。在stackframe.h中这样地定义了get_sp,当k1不是0的时候,置sp为TIMESTACK,否则1.sp在内核区地址不改变2.sp不在内核区跳转到KERNEL_SP。(内核区仅指ULIM以上)

get_sp会在SAVE_ALL的时候调用,SAVE_ALL会把真正的sp存进TF_REG29,调用的get_sp是为了找到存TF寄存器的栈的位置。所以或许下面代码中k1就是在判断是否是时钟中断,而TIMESTACK的意义就是时钟中断的栈的位置。

在env_destroy有这样一条语句

 

如果当前进程被毁灭的话,在sched_yield选好的下一个进程跳转的时候需要保存的环境应该是KERNEL_STACK的环境,即当前进程被调度之前的环境。所以KERNEL_SP是系统调用后的环境,TIMESTACK是时钟中断后的环境。

 当我们使用sched.c中的函数切换进程时有两种可能:

1.现在是第一个进程或者要切换进程和现在的进程一样

2.不是第一个进程也和curenv不一样

如果是第一种情况的话不需要把TIMESTACK中的环境存到curenv->tf,第二种情况的话需要做这个操作。env_pop_tf里把当前环境设置成取出的进程上一次结束的环境。

在我们的extra中毁灭一个进程会再一次调度sched_yield,所以环境会正常更新。如果所有进程都被毁灭的话,恢复的环境是Kernel Stack的环境,即第一个进程被调度之前的环境。

四、异常

//未完待续,或许就鸽了

转载于:https://www.cnblogs.com/puublog/p/10707188.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值