操作系统课程作业要求,针对linux 2.6的内核(kernel)进行开发,详细的要求和材料可以参照课程连接:
CS3 OPERATING SYSTEMS, PRACTICAL EXERCISE
PHASE 1 PHASE 2 主要为如何在DICE机上使用VB和linux内核排程器(scheduler)的背景知识阅读, PHASE 3 是正式的开发,课程评分也只参考这一环节,而这一环节也分为三个小部分,简单概括:1.简单的编译与载入、卸载内核模块;2.解决SMP的Lost Wake-up问题;3.编写一个副排程器以缓解CPU和I/O的速度冲突。
Part1:
首先将现成的.c文件编译成.ko文件。
获得ko文件后,比较常用的指令:
-insmod worker.ko 加载某一模块
-lsmod 显示当前运作的模块
-rmmod worker 某一模块
课程提供的worker.c编译加载后会运行一个内核例程(kernel routine),每10秒输出一条命令。在卸载时会调用clean_up函数,并在内核例程结束后,clean_up才结束。简单修改worker.c,如加一点有个性化的输入,形成worker1.c模块,即可提交,30分到手。
Part2:
这次提供的是stuckworker.c,与worker.c不同的是,clean_up函数中,先指示例程结束,然后等了12秒,clean_up才进入sleep的状态,此时已错过了例程的wake_up,简单概括就是一个lost wake-up Problem,在UP中不会出现,但在SMP中有可能出现并造成严重的问题。也不难,课程提供的一个提示是使用wait_even函数,避免了没人唤醒的问题。
Part3:
这一部分挑战最大,主要解决CPU运行速度和I/O运行速度不一致的问题。在读取外设的时候大量的系统资源会被占用,要求开发一个内核,使得在有键盘活动时,读取外设的进程暂停60秒。其中滤出这些占用资源过多的原则是:
1.进程内核运行时间是否大于1秒;
2.进程内核运行时间是否超过总运行时间10%;
struct task_struct *p;
for_each_process(p){
if(cputime_to_secs(p->stime)>limit_time_in_kernel/*此处为1秒*/){//进程内核运行时间
ktime_get_ts(&c_t);//获取当前时间
s_t=(c_t.tv_sec)-(p->start_time.tv_sec);//获取运行时间
if(cputime_to_secs(p->stime)*limit_for_occupy_time/*此处为10*/>s_t){...}//判断进程内核运行时间是否超过总运行时间10%
}
若满足以上两个原则,则暂停进程60秒。寻找这个进程可能不难,但是如何停止,然后恢复却不那么容易,有很多种看似可行的办法。Julian多次发群邮提醒,别想复杂,不用从底层去做,不用通过设置位去完成,从userspace就可以完成。一般而言,在terminal暂停一个进程的指令是kill,那么kill在内核里对应调用的函数为:send_sig(SIGSTOP,,);,相应的恢复运行函数为send_sig(SIGCONT,,);,但在这里内核出了一点小差错,原因尚不明了,可以用kill_pid(task_pid(p, SIGCONT, 1);代替。仅仅这两句代码的正确选择,耗费掉了整个作业的大概80%以上时间。完成正常的停止与恢复后,其他一些附加的小功能也就水到渠成了。
很值得一提的是,OS Lecturer Julian还自己开发了一个麻将游戏!