2019-2020-1 20199307《Linux内核原理与分析》第九周作业
进程的切换和系统的一般执行过程
进程调度的时机
硬中断和软中断
- 中断是指在计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的时间处理程序。待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程
- 引起中断的事件称为中断源。中断源向CPU提出处理的请求称为中断请求。发生中断时被打断程序的暂停点称为断点。CPU暂停现行程序而转为响应中断请求的过程称为中断响应。处理中断源的程序称为中断处理程序。CPU执行有关的中断处理程序称为中断处理。而返回断点的过程称为中断返回。中断的实现由软件和硬件综合完成,硬件部分叫做硬件装置,软件部分称为软件处理程序
硬中断
- 由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。
软中断
- 为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成
异常
- 故障(Fault):出现问题,可以恢复到当前指令。
- 退出(Abort):不可恢复的严重故障,导致程序无法继续运行,只能退出。
- 陷阱(Trap):程序主动产生的异常。
进程调度时机
schedule函数
- linux内核通过schedule函数实现进程调度,schedule函数在运行队列中找到一个进程,把CPU分给它。所以调用schedule函数一次就是调度一次,调用schedule函数的时候就是进程调度时机。
- 调用schedule函数的两种方法如下:
- 进程主动的调用schedule():如进程调用阻塞的系统调用等待外设或主动睡眠等,最终都会在内核中调用schedule函数。
- 松散调用schedule():内核代码中可以随时调用schedule函数使当前内核路径让出CPU。
上下文
- 一般来说,CPU在任何时刻都处于以下三种情况之一
- 运行于用户空间,执行用户进程上下文
- 运行于内核空间,处于进程(一般是内核线程)上下文。
- 运行于内核空间,处于中断(中断处理程序ISR,包括系统调用处理过程)上下文。
- Linux内核工作在进程上下文或者中断上下文。提供系统调用服务的内核代码代表发起系统调用的应用程序运行在进程上下文;另一方面,中断处理程序,异步运行在中断上下文。中断上下文和特定进程无关。
- 运行在进程上下文的内核代码是可以被抢占的(Linux2.6支持抢占)。但是一个中断上下文,通常都会始终占有CPU(当然中断可以嵌套,但我们一般不这样做),不可以被打断。正因为如此,运行在中断上下文的代码就要受一些限制,不能做下面的事情:
- 睡眠或者放弃CPU:这样做的后果是灾难性的,因为内核在进入中断之前会关闭进程调度,一旦睡眠或者放弃CPU,这时内核无法调度别的进程来执行,系统就会死掉
- 尝试获得信号量:如果获得不到信号量,代码就会睡眠,会产生和上面相同的情况
- 执行耗时的任务:中断处理应该尽可能快,因为内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响系统功能。
- 访问用户空间的虚拟地址:因为中断上下文是和特定进程无关的,它是内核代表硬件运行在内核空间,所以在终端上下文无法访问用户空间的虚拟地址
进程调度的时机
- 简单总结进程调度时机如下:
- 用户进程通过特定的系统调用主动让出CPU
- 中断处理程序在内核返回用户态时进行调度
- 内核线程主动调用schedule函数让出CPU
- 中断处理程序主动调用schedule函数让出CPU,涵盖第一和第二种情况
调度策略与算法
- 在操作系统中调度是指一种资源分配,因而调度算法是指:根据系统的资源分配策略所规定的资源分配算法。对于不同的的系统和系统目标,通常采用不同的调度算法。
- 调度策略
- 首先考虑这个算法的整体目标,是追求资源利用率最高,还是追求相应最即时,或者是追求其他的特定目标?为了满足定下的这些目标,就需要找对应的方法或机制作为对策,这就是调度策略。
- 调度算法
- 接下来考虑如何实现调度策略并满足设定的目标。用数组、链表、图,还是树来存储就绪进程呢?在加入就绪队列时就排序,还是调度时再排序?这些具体的实现就是调度算法。
进程的分类(以下介绍两种和调度相关的分类方式)
##进程上下文切换 - 为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换 - 挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行 - 进程上下文包含了进程执行需要的所有信息 - 用户地址空间: 包括程序代码,数据,用户堆栈等 - 控制信息 :进程描述符,内核堆栈等 - 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同) - schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换 - next = pick_next_task(rq, prev);//进程调度算法都封装这个函数内部 - context_switch(rq, prev, next);//进程上下文切换 - switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程 ##linux系统的一般运行过程 - Linux系统的一般执行过程最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程 1.正在运行的用户态进程X 2.发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack). 3.SAVE_ALL //保存现场 4.中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换 5.标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行) 6.restore_all//恢复现场 7.iret-pop cs:eip/ss:esp/eflags from kernel stack 8.继续运行用户态进程Y ##linux系统构架与执行过程概览 ###linux操作系统架构概览 ###最简单也是最复杂的操作--执行ls命令 ###从CPU和内存的角度看Linux系统的执行 - CPU执行指令的角度: - 内存的角度:进程调度相关源代码跟综和分析
实验要求
1.理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否准确;
2.使用gdb跟踪分析一个schedule()函数 ,验证您对Linux系统进程调度与进程切换过程的理解;推荐在实验楼Linux虚拟机环境下完成实验;
3.特别关注并仔细分析switch_to中的汇编代码,理解进程上下文的切换机制,以及与中断上下文切换的关系;
实验过程
1.配置运行MenuOS系统
2.配置gdb远程调试并在schedule、context_switch、pick_next_task设置断点,并使用c继续运行
3.使用list查看断点处的代码
总结
在学习《庖丁解牛》第八章内容后,我了解到进程调度是为了合理分配计算机资源,并让每个进程都获得适当的执行机会。由于进程调度函数schedule是内核态函数,且并非系统调用,故用户态进程只能在发生中断时被动地调度。而内核态线程即可以被动调度,也可以主动发起进程调度。被动进程调度的时机位于发生中断并且系统执行完毕对应的中断服务程序之后。还有,Linux内核调用schedule()函数进行调度,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换。