向xv6添加一个功能,当进程使用CPU时间时,该功能会定期发出警报
这个作业主要是分两步完成:
第一步是添加alarm系统调用,这跟homework 3那里添加date系统调用基本一样,根据hint一步步下去就行。这里就不再赘述。
第二步是设置陷入后内核的处理,即修改trap.c/trap()函数,这个是比较难的。
Hint:Add some new field in struct proc and initialize proc fields in allocproc() in proc.c.
//proc.h/struct proc
int alarmticks; // set the standard for the alarm
int alarminterval; // how many ticks have passed since the last call
void (*alarmhandler)(); // a function pointer
//proc.c/allocproc()/found
// Initialzize some part of ticks
p->alarminterval = 0;
p->alarmticks = -1; //if there is no sys_alarm, it will never be valid
这一步还初始化了alarmticks=-1,真是学到了。
void (*alarmhandler)();
函数指针定义在结构体里,以前也用的少,得多练
Hint: Every tick, the hardware clock forces an interrupt, which is handled in trap() by case T_IRQ0 + IRQ_TIMER; you should add some code here.
Hint: You only want to manipulate a process's alarm ticks if there's a process running and if the timer interrupt came from user space; you want something like
if(myproc() != 0 && (tf->cs & 3) == 3) ...
Hint: In your IRQ_TIMER code, when a process's alarm interval expires, you'll want to cause it to execute its handler. How can you do that?
Hint: You need to arrange things so that, when the handler returns, the process resumes executing where it left off. How can you do that?
//trap.c/trap():
case T_IRQ0 + IRQ_TIMER:
if(cpuid() == 0){
acquire(&tickslock);
ticks++;
wakeup(&ticks);
release(&tickslock);
}
//if there's a process running and if the timer interrupt came from user space
if(myproc() != 0 && (tf->cs & 3) == 3){
myproc()->alarminterval++;
//cprintf("interval %d alarmticks: %d\n",myproc()->alarminterval,myproc()->alarmticks);
if(myproc()->alarminterval == myproc()->alarmticks){
myproc()->alarminterval = 0;
//myproc()->alarmhandler();是不是每个人写这里都会这样写,hahaha
//cprintf("ticks: %d alarmticks: %d\n", ticks,myproc()->alarmticks);
//cprintf("%d alarm!\n",myproc()->alarmticks);
//lapiceoi();
//push the old eip to stack
tf->esp -= 4;
*((uint*)(tf->esp)) = tf->eip;
//what in eip will be the next instruction to execute
tf->eip = (uint)myproc()->alarmhandler;
}
}
lapiceoi();
break;
在trap()中试了好多次myproc()->alarmhandler();
完全没有用,一度以为是结构体中定义错了,结果是这样:
既然
handler在用户态
,那么内核态必然不能执行;需要中断切换回用户态后才能执行。那么,如何使得切换回用户态后,执行handler,执行完毕继续执行原来的代码(old eip)呢?很简单,利用函数返回的机制,save and restore,将原来的eip压栈,然后将eip修改为handler的地址即可。对这一块不太清楚的话,可以去看一下trapasm.S,看一下这部分代码是如何陷入中断,然后返回的。
感谢孟永康
写好了这些之后,发现还是不行,
于是我就把myproc()->alarminterval跟myproc()->alarmticks输出来看看:
发现interval只到6就停了,让我想起了那个hint:
If you only see one "alarm!", try increasing the number of iterations in alarmtest.c by 10x.
我就把alarmtest.c中i<25500000改成i<250500000
结果还是不太对劲,为什么还是只有5个alarm,点点也不规律且比别人多很多,这是为什么?
可能跟虚拟机时钟频率有关?
最后
还有个问题,就是为什么会陷入到case T_IRQ0 + IRQ_TIMER?
系统在固定的时间间隔都发生一次时钟中断,每当计数器溢出的时候,就会产生中断,陷入到case T_IRQ0 + IRQ_TIMER去处理,处理完后在计数器重置,返回原进程。
“时钟中断”是特别重要的一个中断,因为整个操作系统的活动都受到它的激励。系统利用时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU;利用时钟中断进行记帐、监督系统工作以及确定未来的调度优先级等工作。可以说,“时钟中断”是整个操作系统的脉搏。
谢谢Farmwang