[MIT6.828] LAB4 PART A

Exercise 1.Implement round-robin scheduling in sched_yield() as described above. Don't forget to modify syscall() to dispatch sys_yield().

修改kern/sched.c文件的sched_yield()函数

注意:
1、注意起始env的选择一定是当前env的下一个,如果没有env或者env到了最后就从第1个env开始,第0个env作为idle使用的。
2、循环次数,由于不遍历第0个env,所以总共最多循环NENV-1次,这时候整好循环到当前env(如果有的话)



Exercise 2. Implement the system calls described above (sys_exofork, sys_env_set_status, sys_page_alloc, sys_page_map, sys_page_unmap) in kern/syscall.c. You will need to use various functions in kern/pmap.c and kern/env.c, particularly envid2env(). For now, whenever you call envid2env(), pass 1 in the checkperm parameter. Be sure you check for any invalid system call arguments, returning -E_INVAL in that case. Test your JOS kernel with user/dumbfork and make sure it works before proceeding.

修改kern/syscall.c中如下函数

注意返回值的问题,子进程要返回0,所以要把0放到eax中,子进程开始运行的时候从内核返回到用户层就是把eax作为返回值。

 

 


注意status的值要在限定的范围内。

 



注意perm的权限,有些一定要置位,有些一定不能有。



主要还是传入参数的安全性/合法性检测

 

一个page_remove就把活儿轻松搞定

最后还需要注意的是,在syscal()函数里,也要把对应的系统调用安排好。

Challenge 1 Add a less trivial scheduling policy to the kernel, such as a fixed-priority scheduler that allows each environment to be assigned a priority and ensures that higher-priority environments are always chosen in preference to lower-priority environments. If you're feeling really adventurous, try implementing a Unix-style adjustable-priority scheduler or even a lottery or stride scheduler. (Look up "lottery scheduling" and "stride scheduling" in Google.)

Write a test program or two that verifies that your scheduling algorithm is working correctly (i.e., the right environments get run in the right order). It may be easier to write these test programs once you have implemented fork() and IPC in parts B and C of this lab.

给进程调度加入优先级。这个问题实现起来是很轻松的,在内核开辟一块进程调度队列即可。不过原本代码根本没有考虑优先级,所以调度部分sched_yield()要重写,各种进程状态改变的部分也要加入优先级部分,写起来比较麻烦。但是简单的可以用数组来实现,这样每次进程切换的时候都会遍历一次进程数组,这个简单的实现可以参考XV6的进程优先级实现部分。下面说的是进程队列的实现
1、进程结构体要加入优先级成员,并新建一个进程list类型,或者直接用数组。
2、在内核建立进程队列。
3、在进程创建的时候给与初始优先级。
4、在进程切换的时候,当前进程优先级变为初始值入队,其余进程优先级递增(可加权递增),在进程队列中选择最大的一个运行。
5、进程在变为非runnable(阻塞)的时候要从进程队列删除,恢复的时候加入,为了提高程序的响应速度,可以在恢复的时候增加优先级。

这个模型的优点是实现方便,操作灵活,在并发不多的环境下性能也不错。


Challenge 2 The JOS kernel currently does not allow applications to use the x86 processor's x87 floating-point unit (FPU), MMX instructions, or Streaming SIMD Extensions (SSE). Extend the Env structure to provide a save area for the processor's floating point state, and extend the context switching code to save and restore this state properly when switching from one environment to another. The FXSAVE and FXRSTOR instructions may be useful, but note that these are not in the old i386 user's manual because they were introduced in more recent processors. Write a user-level test program that does something cool with floating-point.
给中断加入保存浮点寄存器的功能。
1、给inc/trap.h文件中的Trapframe结构体新增char tf_fpus[512]成员,并新增uint32_t tf_padding0[3]用来对齐。
2、修改kern/trapentry.S文件中的_alltraps函数,加入保存fpu寄存器的功能

3、修改kern/env.c文件env_pop_tf()函数,加入恢复fpu功能

4、测试用例是修改的user/dumbfork.c的umain()函数。

注意
1、利用父子进程不停的切换来测试浮点寄存器有没有被保存。
2、gcc 4.5.1 x86版本 -03优化好像对于浮点运算提供了保护功能,如果运算后有函数调用会浮点数保存回去(或者是没有达到优化条件?),这样即使函数内部使用了浮点寄存器也不会影响到外面。所以只好用嵌入汇编测试了,不知道有没有编译参数可以调整。当我把上面循环手工展开,把sys_yield()改成直接inline到系统调用时,只用C代码也可以达到测试的效果。

Challenge 3 Add the additional system calls necessary to read all of the vital state of an existing environment as well as set it up. Then implement a user mode program that forks off a child environment, runs it for a while (e.g., a few iterations of sys_yield()), then takes a complete snapshot or checkpoint of the child environment, runs the child for a while longer, and finally restores the child environment to the state it was in at the checkpoint and continues it from there. Thus, you are effectively "replaying" the execution of the child environment from an intermediate state. Make the child environment perform some interaction with the user using sys_cgetc() or readline() so that the user can view and mutate its internal state, and verify that with your checkpoint/restart you can give the child environment a case of selective amnesia, making it "forget" everything that happened beyond a certain point.

增加系统调用,提供进程快照的功能。要提供完整的快照功能,除了要保存进程的全部数据,如果考虑到进程不掉用exec来替换代码文本外,需要保存以下数据
1、进程全局变量
2、进程用户栈
3、进程环境变量Env结构体
内核栈数据不需要保存,因为没有内核抢占功能,内核栈是共用的,如果想保存全局变量的话需要修改载入程序文件处的函数用来保存一些信息,而且不同的全局变量所在段大小是不同的,在用户空间分配也比较麻烦,所以这里暂时只保存用户栈和Env结构体,也可以实现简单的进程快照功能。具体实现如下:
1、在inc/env.h中声明进程状态的结构体

2、修改inc/syscall.h中的enmu,增加sys_proc_sav和sys_proc_rst两个调用号

3、在kern/syscall.c中新增两个函数sys_proc_save和sys_proc_restore分别用来保存和恢复进程状态,并且修改syscal()函数,使得这两个函数处理新增的两个调用号

4、修改inc/lib.h文件,声明两个系统调用的用户接口

5、修改lib/syscall.c文件,实现两个系统调用的用户接口

6、在user/dumbfork.c的基础上修改umain()函数用来测试。


注意:
1、把进程快照保存在用户空间的好处就是父进程可以非常方便的修改用户状态,不好之处就是风险实在太大。也可以把真实数据放入内核,外界用ID代替,就像envid和filedesc一样。
2、这里把进程快照变量定义成了全局变量,不能放在栈上,因为它还要保存的栈数据呢,这样一来就超过栈的大小了。其实也可以实现成在内核分配正好大小的数据返回指针给用户空间,不过由于目前内核对用户空间的内存管理还不够方便(还没实现堆管理),所以就没这么做。

Question 1. In your implementation of env_run() you should have called lcr3(). Before and after the call to lcr3(), your code makes references (at least it should) to the variable e, the argument to env_run. Upon loading the %cr3 register, the addressing context used by the MMU is instantly changed. But a virtual address (namely e) has meaning relative to a given address context--the address context specifies the physical address to which the virtual address maps. Why can the pointer e be dereferenced both before and after the addressing switch?

因为无论是e变量(保存在内核栈上)还是e指向的环境结构体变量(保存在内核数据段)其页映射关系在每个进程的cr3对应的页目录中都是一致的,所以切换cr3不会引发此方面的异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
实验概述 本次实验是MIT 6.828操作系统课程的第一次实验,主要内容是编写一个简单的操作系统内核,并在QEMU虚拟机上运行。本次实验共有9个练习,其中练习9要求实现一个简单的用户程序并运行。 练习9要求我们实现一个简单的用户程序,该程序能够在屏幕上输出一些信息,并等待用户输入,输入结束后将输入内容输出到屏幕上。用户程序的具体要求如下: - 输出一些信息,例如“Hello World!”。 - 等待用户输入,可以使用getchar()函数实现。 - 将用户输入内容输出到屏幕上。 实验过程 1. 编写用户程序 我们首先在lab1目录下创建一个user文件夹,用于存放用户程序。然后创建一个名为“test.c”的文件,编写用户程序的代码如下: ``` #include <stdio.h> int main() { printf("Hello World!\n"); char c = getchar(); printf("You entered: %c\n", c); return 0; } ``` 这段代码的功能是输出“Hello World!”并等待用户输入,输入结束后将输入内容输出到屏幕上。 2. 修改Makefile文件 为了能够编译用户程序,我们需要修改Makefile文件。具体修改如下: ``` UPROGS=\ _cat\ _echo\ _forktest\ _grep\ _init\ _kill\ _ln\ _ls\ _mkdir\ _rm\ _sh\ _stressfs\ _usertests\ _wc\ _test\ # 添加用户程序的名称 $(OBJDIR)/_test: $(OBJDIR)/test.o $(LIBDIR)/ulib.o | $(OBJDIR) $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^ $(OBJDIR)/test.o: test.c | $(OBJDIR) $(CC) $(CFLAGS) -c -o $@ $< ``` 在UPROGS变量中添加上刚刚编写的用户程序的名称“_test”,然后在Makefile文件的末尾添加如上代码。 3. 编译内核和用户程序 在终端运行命令“make”,编译内核和用户程序。 4. 运行QEMU虚拟机 在终端运行命令“make qemu”,启动QEMU虚拟机。 5. 运行用户程序 在QEMU虚拟机中,输入“test”,即可运行刚刚编写的用户程序。运行结果如下: ``` Hello World! This is a test. You entered: T ``` 可以看到,程序首先输出了“Hello World!”这个信息,然后等待用户输入。我们输入了“This is a test.”这个字符串,然后按下回车键,程序将输入内容输出到了屏幕上。 实验总结 本次实验要求我们实现一个简单的用户程序并运行。通过编写代码、修改Makefile文件、编译内核和用户程序、启动虚拟机以及运行用户程序等步骤,我们成功地完成了本次实验,并学会了如何在操作系统内核中运行用户程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值