1 系统调用
宏观上 Linux 操作系统的体系架构分为⽤户态和内核态。计算机的硬件资源是有限 的,为了减少有限资源的访问和使⽤冲突,CPU 和操作系统必须提供⼀些机制对⽤户 程序进⾏权限划分。现代的 CPU ⼀般都有⼏种不同的指令执⾏级别,就是什么样的程 序执⾏什么样的指令是有权限的。在⾼的执⾏级别下,代码可以执⾏特权指令,访问 任意内存,这时 CPU 的执⾏级别对应的就是内核态,所有的指令包括特权指令都可以 执⾏。相应的,在⽤户态(低级别指令),代码能够掌控的范围会受到限制。为什么 会出现这种情况呢?其实很容易理解,如果没有权限级别的划分,系统中程序员编写 的所有代码都可以使⽤特权指令,系统就很容易出现崩溃的情况。因为不是每个程序 员写的代码都那么健壮,或者说会⾮法访问其他进程甚⾄内核的资源,就会产⽣信息 安全问题,这也是操作系统发展的过程中保证系统稳定性和安全性的⼀种机制。让普 通程序员写的⽤户态的代码很难导致整个系统的崩溃,⽽操作系统内核的代码是由更 专业的程序员写的,有规范的测试,相对就会更稳定、健壮。
有了⽤户态、内核态和中断处理过程的概念之后,下⾯以系统调⽤为例来看中断 服务具体是怎么执⾏的。系统调⽤的意义是操作系统为⽤户态进程与硬件设备进 ⾏交互提供了⼀组接⼝。
2 进程和进程状态
进程是进程实体的运行过程, 是系统进行资源分配和调度的一个独立单位 。进程是由PCB、 程序段和数据段三部分组成的,进程控制块PCB是名字为task_struct的数据结构, 它称为任务结构体 。任务结构体中容纳了一个进程的所有信息, 是系统对进程进行管理和控制的有效手段, 是系统实现进程调度的主要依据。
Linux中每一个进程由一个task_struct数据结构来描述。进程描述符放在动态内存中而且和内核态的进程栈放在一个独立的8KB的内存区中。linux系统为每个用户进程分配了两个栈: 用户栈和内核栈。 当一个进程在用户空间执行时,系统使用用户栈; 当在内核空间执行时, 系统使用内核栈。 由于内核栈地址空间的限制, 内核栈不会分配很大的空间。 此外, 内核进程只有内核栈, 没有用户栈。
进程的状态如下:
- 运行态: 进程正在使用CPU运行的状态。
- 可运行态: 进程已分配到除CPU外所需要的其它资源, 等待系统把CPU分配给它之后即可投入运行。
- 等待态: 又称睡眠态, 它是进程正在等待某个事件或某个资源时所处的状态。
- 暂停态: 进程需要接受某种特殊处理而暂时停止运行所处的状态。
- 僵死态: 进程的运行已经结束, 但它的任务结构体仍在系统中。
3 进程调度
进程调度的功能:
(1)记录系统中所有进程的执行情况。
(2)选择占有处理机的进程。
(3)进行进程上下文切换。—个进程的上下文(context)包括进程的状态、有 关变量和数据结构的值、机器寄存器的值和PCB以及 有关程序、数据等。
进程调度的时机:
(1)进程状态发生变化时。
(2)当前进程时间片用完。
(3)进程从系统调用返回到用户态。
(4)中断处理后,进程返回到用户态。
Linux进程调度采取的是动态优先级法,调度的对象是可运行队列。在调度过程中,调度程序检查可运行队列中所有进程的权值, 选择其中权值最大的进程做为下一个运行进程。
Linux系统的一般执行过程举例:
• 以32位x86系统结构linux-3.18.6为例,以系统调⽤作为特殊的中断简要总结如下。 • (1)正在运⾏的⽤户态进程X。 • (2)发⽣中断(包括异常、系统调⽤等),CPU完成以下动作。 •
• save cs:eip/ss:esp/eflags:当前CPU上下⽂压⼊进程X的内核堆栈。
• load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack):加载当前进程内核堆栈相关信息,跳转到中断处理程序,即中断执⾏路径的起点。 • (3)SAVE_ALL,保存现场,此时完成了中断上下⽂切换,即从进程X的⽤户态到进程X的内核态。
•
• (4)中断处理过程中或中断返回前调⽤了schedule函数,其中的switch_to做了关键的进程上下⽂切换。将当前进程X的内核堆栈切换到进程调度算法选出来的next进程 (本例假定为进程Y)的内核堆栈,并完成了进程上下⽂所需的EIP等寄存器状态切换。详细过程⻅前述内容。
• (5)标号1,即前述3.18.6内核的swtich_to代码第50⾏“”1: “ ”(地址为switch_to中的“$1f”),之后开始运⾏进程Y(这⾥进程Y曾经通过以上步骤被切换出去,因此可以 从标号1继续执⾏)。
• (6)restore_all,恢复现场,与(3)中保存现场相对应。注意这⾥是进程Y的中断处理过程中,⽽(3)中保存现场是在进程X的中断处理过程中,因为内核堆栈从进程X 切换到进程Y了。
• (7)iret - pop cs:eip/ss:esp/eflags,从Y进程的内核堆栈中弹出(2)中硬件完成的压栈内容。此时完成了中断上下⽂的切换,即从进程Y的内核态返回到进程Y的⽤户 态。
• (8)继续运⾏⽤户态进程Y。
4 关于Linux的启动过程简述
1.启动并加载BIOS,对硬件进行检测,查找并加载MBR。
2.找到MBR后,存储BootLoader信息,加载GRUB。
3.查找并加载kernel。
4.装载驱动,挂载rootfs,执行/sbin/init。
5.OS初始化后,执行runlevel程序。
6.启动指定级别的服务。