2019-2020-1 20199310《Linux内核原理与分析》第九周作业
1.问题描述
在前面的文章中,学习了译链接的过程和ELF可执行文件格式,对Linux内核装载和启动一个可执行程序,本次内容围绕对进程调度的时机和进程切换进行,分析进程的调度时机,调度策略和算法,并跟踪schedule,pick_next_task和context_switch等函数。
2.解决过程
2.1 进程调度的时机
中断:进程调度的时机与中断相关,是程序执行过程中的强制性转移,转移到操作系统内核相应的处理程序。中断在本质上都是软件或者硬件发生了某种情形而通知处理器的行为,处理器进而停止正在运行的指令流,对这些通知作出相应反应,即转去执行预定义的中断处理程序。
2.1.1 硬中断
就是CPU的两根引脚(可屏蔽中断和不可屏蔽中断)。CPU在执行每条指令后检测这两根引脚的电平,如果是高电平,说明有中断请求,CPU就会中断当前程序的执行去处理中断。
2.1.2 软中断/异常
包括除零错误、系统调用、调试断点等在CPU执行指令过程中发生的各种特殊情况统称异常。异常会导致程序无法继续执行,而跳转到CPU预设的处理函数。
2.1.3 schedule函数
Linux内核通过schedule函数实现进程调度,schedule函数在运行队列中找到一个进程,把CPU分配给它,调用schedule函数的时候就是进程调度的时机。
schedule函数调用的两种方法:主动调用和松散调用。
2.1.4 上下文
CPU所处的3种情况:
(1)运行于用户空间,执行用户进程上下文。
(2)运行于内核空间,处于进程上下文。
(3)运行于内核空间,处于中断上下文。
内核线程以进程上下文的形式运行在内核空间中,本质上还是进程,但它有调用内核代码的权限,如主动调用schedule()函数让出CPU。
2.1.5 调度时机
(1)用户进程通过特定的系统调用主动让出CPU。
(2)中断处理程序在内核返回用户态时进程调度。
(3)内核线程主动调用schedule函数让出CPU。
(4)中断处理程序主动调用schedule函数让出CPU。
2.2 调度策略与算法
调度算法就是从就绪队列中选一个进程。
调度策略:确定算法目标时追求资源利用率最高,还是追求相应最即时。
调度算法:调度策略的具体实现。
2.2.1 进程分类
根据处理对象,进程可分为I/O消耗型进程,服务器的服务进程和处理器消耗型进程;根据处理方式,也可分为交互式进程,批处理进程和实时进程。
2.2.2 调度策略
#define SCHED_NORMAL 0 //普通进程
#define SCHED_FIFO 1 //实时进程
#define SCHED_RR 2 //实时进程
#define SCHED_BATCH 3 //保留,未实现
#define SCHED_IDLE 5 //idle进程
Linux中的几种调度策略为SCHED_NORMAL,SCHED_FIFO和SCHED_RR,其中SCHED_NORMAL是用于普通进程的调度类,而SCHED_FIFO和SCHED_RR是用于实时进程的调度类,优先级高于SCHED_NORMAL。
2.2.3 CFS调度算法
CFS即为完全公平调度算法,其基本原理是基于权重的动态优先级调度算法,每个进程使用CPU的顺序由进程已使用的CPU虚拟时间决定,已用的虚拟时间越少,进程排序就越靠前,进程再次被调度执行的概率也就越高。每个进程每次占用CPU后能够执行的时间由进程的权重决定,并保证在某个时间周期内运行队列里的所有进程都能够至少被调度执行一次。
2.3 进程上下文切换
进程切换:为了控制进程的执行,内核必须有能力挂起正在CPU中运行的进程,并恢复执行以前挂起的某个进程,也称为任务切换或进程上下文切换。
进程上下文包括:用户地址空间,控制信息和硬件上下文。
进程切换的基本步骤:
(1)切换页全局目录(CR3)以安装一个新的地址空间,这样不同进程的虚拟地址如0x8048400就会经过不同的页表转换为不同的物理地址。
(2)切换内核态堆栈和硬件上下文,因为硬件上下文提供了内核执行新进程所需要的所有信息,包含CPU寄存器状态。
进程切换关键环节分析示意图如下:
2.4 schedule()函数进程调度相关代码跟踪和分析
配置MenuOS系统,重新编译内核启动:
打开gdb进行远程调试,设置schedule,context_switch和pick_next_task三个断点:
单步跟踪及对应函数代码:
在pick_next_task处停留,使用某种调度策略,选择下一个进程来切换:
在context_switch处停留,实现进程的切换:
由于switch_to内部是内嵌汇编代码,故无法跟踪调试,其实现关键的上下文切换。
3.总结
本文主要学习了进程调度的时机和进程切换进行,分析进程的调度时机,调度策略和算法,并跟踪schedule,pick_next_task和context_switch等函数。Linux系统的一般运行过程为用户态进程X发生中断,由硬件完成将当前CPU上下文压入用户态进程X的内核堆栈,加载当前进程内核堆栈相关信息,挑战到中断处理程序,即中断执行路径的起点。保存现场,在中断处理过程中或中断返回前调用schedule函数,switch_to做了进程上下文切换,之后开始运行用户态另一进程Y,恢复现场,从Y进程的内核堆栈中弹出进程X的内核堆栈相关信息,完成中断上下文的切换,继续运行用户态进程Y。