再次认识进程切换

我们在第四章已经简单了解过了在内核启动过程中进程切换的过程,在这一节我们再来重新回顾一下这些内容,并且深入讨论下其中的几个细节。

首先我们需要考虑的是,什么时候可以进行进程的切换。这里可以主要分为下面几种情况:

  1. 进程主动放弃当前的CPU资源,比如显式调用waitsleep通知操作系统当前进程需要等待

  2. 进程想要获取的资源当前不可用,比如尝试获得未被释放的锁,或进行磁盘操作的时候

  3. 进程由于外部中断被打断进入内核态,内核发现某些条件满足(比如当前进程时间片用尽),进行进程切换

在我们实现的ucore中,内核进程是不可抢占的。这也就意味着当内核执行的时候,另一个内核进程不可以夺走它的的CPU资源。但这是不是就意味着一个内核进程执行的时候,它就会一直执行到结束呢?虽说内核进程不可以抢占,但是它可以主动放弃自己占有的CPU资源。如果不这样设计的话,内核当中很有可能出现各种死锁导致内核崩溃。

另一方面,内核不能相信用户进程不会无限执行下去,所以需要提供手段在用户进程执行的时候打断他,比如时钟中断。在ucore的实现中,用户进程可以随时被打断进入内核,操作系统会检查当前进程是否需要调度,从而把运行的机会交给别的进程。

由于内核进程是不可抢占的,所以我们在内核中有许多地方使用了显式的函数调用来进行调度,主要有以下几个地方:

函数

原因

proc.c/do_exit

用户进程退出,放弃CPU资源

proc.c/do_wait

用户进程等待,放弃CPU资源

proc.c/init_main

init线程会等待所有的用户线程执行完毕,之后调用kswapd内核线程回收内存资源

proc.c/cpu_idle

idle线程等待处于就绪态的线程,如果有就调用schedule

sync.c/lock

如果获取锁失败就进入等待

trap.c/trap

用户进程在被中断打断后,内核会检查是否需要调度,如果是则调用调度器进行调度

当用户进程A发生中断或系统调用之后,首先其中断帧会被保存,CPU进入内核态,执行中断处理函数。在执行完毕中断处理函数后,操作系统检查当前进程是否需要调度,如果需要,就把当前的进程状态保存,switch到另一个进程B中。注意在执行上面的操作的时候,进程A处于内核态,类似的,调度后我们到达的是进程B的内核态。进程B从系统调用中返回,继续执行。如果进程B在中断或系统调用中被调度,控制权可能转交给进程A的内核态,这样进程A从内核态返回后就可以继续执行之前的代码了。

最后更新于