理解进程调度时机跟踪分析进程调度与进程切换的过程
一、基础知识
(一)进程调度的时机
1、关键问题
何为进程切换?就是进程调度时机到来时从就绪进程队列中能够挑选一个进程执行,占用CPU时间,那么就有两个关键问题:一是什么时间去挑选一个就绪进程(调度时机);而是如何让进程占用CPU?(进程切换的过程)
2、硬中断与软中断
- 硬中断(Interrupt)就是CPU的两根引脚(可屏蔽中断和不可屏蔽中断)。CPU在执行每条指令后检测两根引脚的电平,如果是高电平,说明有中断请求,CPU就会中断当前程序的执行后去处理中断。一般类似时钟/键盘/硬盘等外设都是以这种方式与CPU进行信号传递的。
- 软中断(Exception)也被称为异常,其包括除零错误/系统调用/调式断点等在CPU执行指令过程中的发生的各种特殊情况统称为异常,异常会导致程序无法继续执行,而跳转到CPU预设的处理函数。异常包括故障/退出/陷阱三种。
3、进程调度时机
(1)schedule函数
Linux内核通过schedule函数实现进程调度,它在运行队列中找到一个进程,把CPU分配给它。所以调用该函数一次就是调度一次,调用该函数的时机就是进程调度的时机。调用schedule函数的两种方法如下:
进程主动调用schedule(),如进程调用阻塞的系统调用等待外设或者主动睡眠。
松散调用,内核代码可以随时根据需要调用schedule使当前内核路径让出CPU。
(2)上下文
一般来说,CPU在任何时刻都处于以下三种情况之一:
运行于用户空间,执行用户进程上下文。
运行于内核空间,处于进程上下文。
运行于内核空间,处理中断上下文。
(3)进程调度的时机
用户进程通过特定的系统调用主动让出CPU。
中断处理程序在内核返回用户态时进行调度。
内核线程主动调用schedule函数让出CPU。
中断处理程序主动调用schedule函数让出CPU。
(二)Linux系统的运行过程
1、Linux的一般执行过程
- (1)正在运行的用户态X。
- (2)发生中断(包括异常、系统调用)。
- (3) 保护现场,此时完成上下文切换。
- (4)中断处理过程中或中断返回前调用schedule函数,其中switch_to做了关键进程上下文切换。
- (5)运行用户态进程Y。
- (6)恢复现场。
- (7)完成中断上下文切换,从进程Y内核态返回到进程Y的用户态。
- (8)继续运行用户态进程Y。
2、几种特殊情况
- (1)通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
- (2)内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;
- (3)创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;
- (4)加载一个新的可执行程序后返回到用户态的情况,如execve;
3、Linux整体框架和执行过程
二、实验过程
在虚拟机环境中打开一个shell命令窗口,按照之前的方式对MenuOS进行调试,在gdb中设置以下断点:
- schedule:进程调度的主体函数
- context_switch:实现进程切换的函数
- pick_next_task:负责根据调度策略和调度算法选择下一个进程
- switch_to:其中switch_to为宏定义,不能设置断点,需到context_switch函数中单步执行查看调用
步骤截图如下所示:
三、实验总结
在本次实验过程中没有遇到类似以往出现的问题,其中最重要的是对于进程上下文切换和进行调度的理解与分析。通过实验可知schedule()函数用来选择一个新的进程来运行,并调用context_switch()进行上下文的切换,这个宏调用switch_to()来进行关键上下文切换,其中pick_next_task()函数封装了进程调度算法。结合本科学习的操作系统的知识,通过动手实验能够使自己对于进程切换的过程有更深的认识,在linux中,进程主动调度的时机可以在中断处理过程中、内核线程中,但用户态进程无法实现主动调度,仅能通过陷入内核态的新时机点进行调度,即在中断处理过程中进行调度。中断在本质上都是软件或某些硬件发生了某情形的而通知处理器的行为,处理器进而停止正在运行的指令流,对这些通知做出相应的反应,级转去执行预定义的中断处理程序。除了主动让出CPU外,进程调度都需要在进程外进行,这就需要从进程的指令流里切换出来,中断能起到切出进程指令流的作用,中断处理程序是与进程无关的内核指令流。运行完内核代码后,CPU顺带检查一下是否需要进程调度。需要则切换进程,不需要则一路顺着函数调用堆栈正常中断返回iret,这样就自然回到原进程继续运行了。