Linux是一种开源电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。Linux是一个一体化内核(monolithic kernel)系统。“内核”指的是一个提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件。如图1所示:
图1 Linux内核(图片来自百度)
操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件。一个计算机系统是一个硬件和软件的共生体,它们互相依赖,不可分割。计算机的硬件,含有外围设备、处理器、内存、硬盘和其他的电子设备组成计算机的发动机。但是没有软件来操作和控制它,自身是不能工作的。完成这个控制工作的软件就称为操作系统,在Linux的术语中被称为“内核”,也可以称为“核心”。Linux内核的主要模块(或组件)分以下几个部分:存储管理、CPU和进程管理、文件系统、设备管理和驱动、网络通信,以及系统的初始化(引导)、系统调用等。(本段内容参考百度百科)
进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。操作系统内核运行中,以及为高层应用程序的运行提供底层支持过程中,进程都发挥着重要作用。进程是操作系统进行资源分配及调度的一个单位。所以我们可以看出一个进程具体是怎么执行的,关键是看操作系统能够为其提供完备的资源(具体包括硬件资源和系统资源)。进程在执行过程中,每个进程都需要操作系统为其分配相应的堆栈空间,日常中用到的计算机每时每刻都会有多个进程再“并行”执行着,对于单核CPU来说,要实现多个进程的并行执行,就需要为每个要执行的进程分配相应的CPU以及其他资源。这显然与单核CPU的执行原理是自相矛盾的。所以,后来就出现了时间片轮转法,使得多个进程时可以在单核CPU的控制下“并行”执行的,分配具体的说就是每个进程按时间段来使用CPU资源,从宏观上看,每个进程是“并行”执行的,但从微观上看,每个进程还是串行执行的。
时间片轮转法的实现是有操作系统来实现的。具体说是又操作系统内核来实现的。所以掌握操作系统内核的工作原理、代码分析是深入学习操作系统领域技能的关键步骤。
实验中用到的mykernel代码中简化了的Linux内核代码。Mykernel代码中具备了Linux操作系统的进程调度的关键部分。掌握这部分代码的运行机制,就会使我们了解Linux操作系统的核心功能。
mykernel代码主要包括三个子文件:结构如图所示:
图4 mykernel代码结构
分析:这段代码显示了mymain.c程序的总体框架。首先是新建了一个PCB结构的任务数组(line16——line23),初始化一个进程0(PID初始化为0,state初始化为0,紧接着初始化ip,sp等),for循环(line25——line33)这段代码是继续创建上下的进程,总共创建了4个进程(MAX_TASK_NUM=4),然后把这4个PCB结构的控制块连接成链表结构。下面到了嵌入式汇编结构(line36——line46),这部分代码主要初始化EIP,EBP,ESP寄存器,使得进程0能够直接被执行,这段汇编代码中,先将堆栈指针sp赋给了ESP寄存器,然后将堆栈指针sp内容压栈,之后将指令指针ip的内容也压栈,下一条指令是ret,它是将当前栈中ESP所指的内容出栈到EIP中,当前ESP所指的内容就是前一条指令压栈进去的ip的值,现在使得EIP寄存器的内容就是进程0的入口地址(ip内容),从而使得进程0能够被执行。
分析:上面这段代码就是进程执行时候的实际运行内容,修改if语句中的内容,可以调控print语句的输出速度(i循环1000000次,调度一次,即调用my_schedule()函数,前一进程终止运行,保存现场;后一进程的ip内容传送到EIP寄存器中,使得下一进程开始执行)。
分析:这是函数是一个时间调度函数。换句话说就是实现了时间片轮转的时间约定,每当他执行的时候就是一个进程调度的过程。代码中#if 1表示需要编译器编译后面的内容,#endif表示结束#if的约束控制。每当这段代码执行时,这段程序都要进行编译。
图9 my_schedule代码
分析:这段代码是整个程序中的关键代码部分。代码开始部分,先定义了两个PCB结构的指针prec和next。之后判断能否进行调度。下面的if_else语句是调度的过程,这里分为两种情况, 一种是调度一个已经运行过的进程,另一种情况是调度一个新的未运行过得进程。本次实验主要是熟练进程调度的原理过程,掌握Linux内核的工作原理。决定进程是否要进行调度是由my_time_handler决定的,这个函数是设定时间片大小的函数,当时间片用完之后,进程便开始调度;而且这个函数还决定着进程上下文切换的的设定: my_need_sched = 1;当 my_need_sched = 1时,my_process()函数才能被允许执行my_schedule()函数调度。
my_schedule()函数内容实现了进程调度算法,当需要进程调度时,my_schedule()便选择一个处于就绪状态的进程,用于进程调度。
实验分析了操作系统中进程之间是如何进行调度的。通过对mykernel的源代码分析,体会进程在切换过程中操作系统对进程运行环境的管理。事实上有时候我们可以把它叫做运行框架,也可以说是上下文,现场环境等等。实际上这些东西指的都是程序运行时候的堆栈,运行内存空间的地址,寄存器值的集合。中断上下文和进程上下文的切换是操作系统的“两把剑”,熟练掌握和理解中断上下文和进程上下文,我们才能充分理解Linux内核源码中进程调度的原理,才能为后续学习Linux操作系统做好基础准备。