Round-Robin Scheduling
在 JOS 系统中,我们主要通过一个函数实现进程调度,即 kern/sched.c 中的 sched_yield() 函数,下面对此函数进行说明。
void sched_yield(void)
首先,实验 4 开始的第一个进程 envs[0]就是特殊的 idle 进程。在本函数中,通过搜索 envs 数组寻找一个新的进程去执行,从前一个执行的进程开始,直到找到一个就绪的进程; 当没有就绪进程时,才重新调度 idle 进程。 在完成 sched_yield()之后一定要将此函数加入到系统调用中,允许用户进程调用调度 函数。
Exercise 6. Implement round-robin scheduling in sched_yield() as described above. Don't forget to modify syscall() to dispatch sys_yield().
Make sure to invoke sched_yield() in mp_main.
Modify kern/init.c to create three (or more!) environments that all run the program user/yield.c.
Run make qemu. You should see the environments switch back and forth between each other five times before terminating, like below.
Test also with several CPUS: make qemu CPUS=2.
... Hello, I am environment 00001000. Hello, I am environment 00001001. Hello, I am environment 00001002. Back in environment 00001000, iteration 0. Back in environment 00001001, iteration 0. Back in environment 00001002, iteration 0. Back in environment 00001000, iteration 1. Back in environment 00001001, iteration 1. Back in environment 00001002, iteration 1. ...
After the yield programs exit, there will be no runnable environment in the system, the scheduler should invoke the JOS kernel monitor. If any of this does not happen, then fix your code before proceeding.
If you use CPUS=1 at this point, all environments should successfully run. Setting CPUS larger than 1 at this time may result in a general protection or kernel page fault once there are no more runnable environments due to unhandled timer interrupts (which we will fix below!).
void
sched_yield(void)
{
struct Env *idle;
struct Env *e;
int i, cur=0;
if (curenv) cur=ENVX(curenv->env_id);
else cur = 0;
for (i = 0; i < NENV; ++i) {
int j = (cur+i) % NENV;
if (j < 2) cprintf("envs[%x].env_status: %x\n", j, envs[j].env_status);
if (envs[j].env_status == ENV_RUNNABLE) {
if (j == 1)
cprintf("\n");
env_run(envs + j);
}
}
if (curenv && curenv->env_status == ENV_RUNNING)
env_run(curenv);
sched_halt();
}