2019-2020-1 20199314 《Linux内核原理与分析》第九周作业
第8章 进程的切换和系统的一般执行过程
1 调度的时机
1.1 硬中断和软中断
中断有很多种,都是程序执行过程中的强制性转移,转移到操作系统内核相应的处理程序。中断在本质上都是软件或者硬件发生了某种情形而通知处理器的行为,处理器进而停止正在运行的指令流(当前进程),对这些通知做出相应反应,即转去执行定义的中断处理程序(内核代码)。
ntel定义的中断类型主要有以下几种。
1. 硬中断
2. 软中断
硬中断(interrupt)
硬中断就是CPU的两根引脚(可屏蔽中断和不可屏蔽中断)。CPU在执行每条指令后检测这两根引脚的电平,如果是高电平,说明有中断请求,CPU就会中断当前程序的执行去处理中断。一般外设都是以这种方式与CPU进行信号传递的,如时钟、键盘、硬盘等。
软中断/异常(Exception)
软中断/异常(Exception):包括除零错误、系统调用、调试断点等在CPU执行指令过程中发生的各种特殊情况统称为异常。异常会导致程序无法继续执行,而跳转到CPU预设的处理函数。
异常分为以下三种:
-
故障(Fault):故障就是没有问题了,但可以恢复到当前指令。例如除0错误、缺页中断等。
-
退出(Abort):简单说是不可恢复的严重故障,导致程序无法继续运行,只能退出了。例如连续发生故障(double fault)。
-
陷阱(Trap):程序主动产生的异常,在执行当前指令后发生。例如系统调用(int 0x80)以及调试程序时设置断点的指令(int 3)都属于这类。
1.2 进程调度时机
1.2.1 schedule函数
Linux 内核通过 schedule 函数实现进程调度,schedule 函数在运行队列中找到一个进程,把CPU分配给它。所以调用 schedule 函数一次就是调度一次,调用 schedule 函数的时候就是进程调度的时机。
调用schedule函数有两种方法:
1. 进程主动调用schedule(), 如进程调用阻塞的系统调用等待外设或主动睡眠等,最终都会在内核中调用schedule函数。
2. 松散调用, 内核代码中可以随时带哦用schedule()使用当前路径让出CPU,也可以根据need_resched标记做进程调度。
1.2.2 上下文
一般来说,CPU 在任何时刻都处于以下3种情况之一。
-
运行于用户空间,执行用户进程上下文。
-
运行于内核空间,处于进程(一般是内核线程)上下文。
-
运行于内核空间,处于中断(中断处理程序ISR,包括系统调用处理过程)上下文。
应用程序通过系统调用陷入内核,或者外部设备产生中断时,CPU就处于终端上下文。
由于中断的级别不同,有不可屏蔽中断、可屏蔽中断、陷阱(系统调用)、异常等。为了整个系统的运行效率,中断上下文中调用其他内核代码有一定的限制。
1.2.3 进程调度的时机
进程调度的时机就是内核调用shcedule函数的时机。
进程调度时机如下:
-
用户进程通过特定的系统调用主动让出CPU。
-
中断处理程序在内核返回用户态时进行调度。
-
内核线程主动调用 schedule 函数让出CPU。
-
中断处理程序主动调用 schedule 函数让出CPU,涵盖以上第一种和第二种情况。
2 调度策略与算法
调度算法就是从就绪队列中选一个进程,一般来说就是挑最重要、最需要(最着急)、等了最长时间的(排队)等,和人类排队抢资源很相似。
调度策略:首先考虑这个算法的整体目标,需找对应的方法或者机制。
调度算法:考虑如何实现调度策略并满足设定的目标,具体的实现就是调度算法。
2.1 进程的分类
进程的分类 1:
-
I/O消耗型进程。典型的像需要大量文件读写操作的或网络读写操作的,如文件服务器的服务进程。这种进程的特点就是CPU负载不高,大量时间都在等待读写数据。
-
处理器消耗型进程。典型的像视频编码转换、加解密算法等。这种进程的特点就是CPU占用率为100%,但没有太多硬件进行读写操作。
进程的分类 2:
-
交互式进程。此类进程有大量的人机交互,因此进程不断地处于睡眠状态,等待用户输入,典型的应用比如编辑器VIM。此类进程对系统响应时间要求比较高,否则用户会感觉系统反应迟缓。
-
批处理进程。此类进程不需要人机交互,在后台运行,需要占用大量的系统资源,但是能够忍受响应延迟,比如编译器。
-
实时进程。实时进程对调度延迟的要求最高,这些进程往往执行非常重要的操作,要求立即响应并执行。
根据进程的不同分类,Linux 采用不同的调度策略。当前Linux系统的解决方案是,对于实时进程,Linux 采用FIFO(先进先出)或者Round Robin(时间片轮转)的调度策略。对其他进程,当前Linux采用CFS(Completely Fair Scheduler)调度器,核心思想是“完全公平”。
2.2 调度策略
Linux 系统中的几种调度策略为SCHED_NORMAL、SCHED_FIFO、SCHED_RR。
其中SCHED_NORMAL 是用于普通进程的调度类,而SCHED_FIFO 和SCHED_RR 是用于实时进程的调度类,优先级高于SCHED_NORMAL。
内核中根据进程的优先级来区分普通进程与实时进程,Linux内核进程优先级为0~139,数值越高,优先级越低,0为最高优先级。
实时进程的优先级取值为0~99;而普通进程只具有nice 值,nice 值映射到优先级为100~139。子进程会继承父进程的优先级。
2.3 CFS调度算法
CFS即为完全公平调度算法,其基本原理是基于权重的动态优先级调度算法。每个进程使用CPU的顺序由进程已使用的CPU虚拟时间(vruntime)决定,已使用的虚拟时间越少,进程排序就越靠前,进程再次被调度执行的概率也就越高。每个进程每次占用CPU 后能够执行的时间(ideal_runtime)由进程的权重决定,并且保证在每个时间周期(_ sched_period)内运行队列里的所有进程都能够至少被调度执行一次。其主要包含以下6个方面。
1.调度周期
2.理论运行时间
3.虚拟运行时间
4.时钟中断周期
5.Linux传统优先级与权重的转换关系是经验值
6.就绪进程排序与存储
3 进程上下文的切换
3.1 进程执行环境的切换
为了控制进程的执行,内核必须有能力挂起正在CPU 中运行的进程,并恢复执行以前挂起的某个进程。这种行为被称为进程切换,任务切换或进程上下文切换。
-
用户地址空间:包括程序代码、数据、用户堆栈等。
-
控制信息:进程描述符、内核堆栈等。
-
硬件上下文,相关寄存器的值。
其中包括了 CR3 寄存器、ESP寄存器、EIP寄存器及其他寄存器等保存和变换。
在实际代码中,每个进程切换基本由两个步骤组成:
1.切换页全局目录(CR3)以安装一个新的地址空间,这样不同进程的虚拟地址如0x8048400 就会经过不同的页表转换为不同的物理地址。
2.切换内核态堆栈和硬件上下文,因为硬件上下文提供了内核执行新进程所需要的所有信息,包含CPU 寄存器状态。
3.2 核心代码分析
schedule() 函数选择一个新的进程来运行,并调用 context_switch 进行上下文的切换。context_switch 首先调用 switch_mm 切换CR3,然后调用宏 switch_to 来进行硬件上下文切换。
4 Linux系统的运行过程
4.1 Linux 系统的一般执行过程
(1)正在运行的用户态进程X。
(2)发生中断(包括异常、系统调用等),硬件完成以下动作。
- save cs:eip/ss:esp/eflags:当前CPU上下文压入用户态进程x的内核堆栈。
- load cs:eip(entry of a specific ISR)and ss:eip(point to kernel stack); 加载当前进程内核堆栈相关信息,跳转到中断处理程序,即中断执行路径的起点。
(3)SAVE_ALL保存现场, 此时完成了中断上下文切换,即从进程X的用户态到进程X的内核态。
(4)中断处理过程中或中断返回前调用了schedule 函数,其中的switch_to 做了关键的进程上下文切换。将当前用户进程X的内核堆栈切换到选出的next进程(本例假定为进程Y)的内核堆栈,并完成了进程上下文所需的EIP等寄存器状态切换。
(5)标号1,即上述代码第50行“1:\t”(地址为swich_to中的“$1f"),之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去,因此可以从标号1继续执行)。
(6) restore_al,恢复现场,与(3)中保存现场相对应。
(7)iret - pop cs:eip/ss:esp/eflags,从Y进程的内核堆找中弹出(2)中硬件完成的压栈内容。此时完成了中断上下文的切换,即从进程Y的内核态返回到进程Y的用户态。
(8)继续运行用户态进程Y。
如上过程在Linux系统中反复执行,其中关键点在于:
-
中断和中断返回有CPU硬件上下文的切换。
-
进程调度过程中有进程上下文的切换
4.2 Linux系统执行过程中的几种特殊情况
-
通过中断处理过程中的调度时机。
-
用户进程向内核线程的切换。
-
内核线程向用户进程的切换。
-
创建子进程的系统调用在子进程中的执行起点及返回用户态的过程较为特殊。
-
加载一个新的可执行程序后返回到用户态的情况也较为特殊。
4.3 操作系统内核
Linux操作系统内核通过中断上下文切换和进程上下文切换这些基本的运行机制来保障Linux操作系统内核为用户提供最基本和重要的服务。
5 Linux系统框架与执行过程概览
5.1 Linux操作系统的构架
任何计算机系统都包含一个基本的程序集合,称为操作系统。操作系统是一个集合,即包含用户态也包含内核态的组件,其主要功能或组件如下:
1. 内核的主要功能
进程管理、进程调度、进程间通信机制、内存管理、中断异常处理、文件系统、I/O系统,网络部分等
2. 其他程序
系统调用基础函数库、Shell程序、系统命令、编译器、解释器、函数库等基础设施。
3. 最关键
CPU、内存、文件。
操作系统的两个目的分界线:
对于底层来说, 与硬件交互管理所有的硬件资源。
对于上层来说, 为用户程序提供一个良好的环境。
Linux操作系统的整体构架如图:
5.2 ls命令执行过程即涉及操作系统相关概念
ls命令的整体执行过程如图:
实验 进程调度相关源代码跟踪和分析
实验运行环境:实验楼
实验过程:
1.配置运行MenuOS系统
启动内核
2. 配置gdb远程调试和设置断点
3.使用gdb跟踪分析schedule函数
遇到第一个断点 schedule函数
遇到第二个断点pick_next_task函数
遇到第三个断点context_switch
但是无法对switch_to跟踪调试,因为其为内嵌汇编代码。