操作系统运行环境与运行机制

一.操作系统的主要工作

  • 程序的执行

    启动程序,执行程序以及程序的结束工作

  • 完成体系结构相关的工作(操作系统与其他软件所不同的地方,与硬件打交道)

  • 完成应用程序所需的共性任务(读盘,申请内存,打印文件)

    提供各种基本服务

  • 性能,安全,健壮等问题

用下图来明确操作系统的的地位:

从图中我们可以看出操作系统是在硬件基础上的第一层扩展。

它底下是硬件, 这个硬件都做了什么工作呢? 怎么样去了解底下的硬件呢?这就是本讲的一个内容之一:操作系统运行环境。 和硬件相关的很多的工作实际上是和操作系统的各个功能相结合的,这里介绍最基本的 CPU 状态: 中断与异常机制。 其他的像虚拟页式存储管理的机制, 设备等等的,会在后面跟操作系统功能结合起来讲。

操作系统完成应用程序当中的一些共性的工作, 向应用程序提供一些基本服务,这是本讲另外一方面的内容:操作系统运行机制,这里重点介绍系统调用机制。


二.处理机的状态

处理器由运算器控制器、一系列的寄存器以及高速缓存构成。
寄存器主要分为两类:

  • 用户可见寄存器:高级语言编译器通过优化算法分配并使用之,以减少程序访问内存次数。
  • 控制和状态寄存器:用于控制处理器的操作通常由操作系统代码使用。

对于用户可见寄存器,高级语言编译器通过优化算法分配这些寄存器,并且使用这些寄存器的主要的目的是为了减少访问内存的次数, 来提高程序的运行效率。

操作系统设计者更加关注的是控制和状态寄存器。 而控制处理器的操作,那么通常只能由操作系统代码来使用。

1.控制和状态寄存器

控制和状态寄存器用于控制处理器的操作,只能在某种特权级别下可以访问、修改。

常见的控制和状态寄存器:

  • 程序计数器(PC:ProgramCounter),记录将要取出的指令的地址。
  • 指令寄存器(IR:InstructionRegister),记录最近取出的指令。
  • 程序状态字(PSW:Program Status Word),记录 处理器的运行状态如条件码、模式、控制位等信息。

2.操作系统的需求——保护

下面来探讨一下操作系统对硬件的需求,其中一个非常重要的需求就是保护。

因为操作系统运行在一个多进程的这样一个环境下,为了支持这些进程的运行。 因此得从操作系统的特征来考虑:并发、 共享

操作系统为多个程序的执行提供了这样一个并发的环境,而多个进程之间又共享操作系统所管理的各种资源。 那么这样一个并发、 共享的计算环境就要求保护 。

其中保护是的:

  • 用户程序与用户程序之间互不干扰
  • 用户程序不对操作系统干扰

这就从操作系统的角度给硬件提出了实现保护、 实现保护控制这个需求。

通常,希望硬件提供一个基本运行机制:CPU 具有一个特权级别,在不同的特权级下可以运行不同的指令集合。 这样把指令分成不同的集合,供操作系统和用户程序分别使用。 通过保护又使得操作系统与用户能够相隔离。 比如说当要访问操作系统空间的时候,那么用户程序是不能够访问的,但是操作系统可以访问用户程序空间, 因此通过一个保护机制达到操作系统与用户的隔离。

有了这样一个需求之后,现代处理器通常把CPU 的状态设计为两种、 三种、 或者是四种。 但是这样在CPU 上时而运行操作系统、时而运行用户程序,CPU 如何知道是运行哪一种状态呢? 这样就有赖于一些寄存器的某些位的设置:通常是在程序状态字寄存器 PSW中专门设置一位或是两位,根据运行程序对资源和指令的权限不同,设置不同的 CPU 状态。

这样不同的程序对资源和指令的使用要求是不同的。

以X86 处理器当中的典型的一个标志寄存器 EFLAGS,EFLAGS 寄存器为例:

其中就有一位IOPL 是 IO 的权限级别。 这个级别用两位来表示四个状态。 除了这个寄存器以外,还有一些 描述符设置了权限级别。 这是硬件提供的各种不同的CPU状态 。

3.特权指令和非特权指令

操作系统需要两种 CPU状态:

  • 内核态(Kernel Mode):运行操作系统程序。
  • 用户态(User Mode):运行用户程序。

因为操作系统只需要两个状态,而这两个状态可以指向不同的指令集合。因此把指令的集合也划分成两类:

  • 特权(privilege)指令:只能由操作系统使用、用 户程序不能使用的指令。
  • 非特权指令:用户程序可以使用的指令。

其实整个指令系统操作系统是都可以使用的,它既可以使用特权指令,也可以使用非特权指令。而用户只能使用这个指令系统当中的一个子集,那么这个子集就是非特权指令。

4.实例:X86系列处理器

X86支持4个处理器特权级别。特权环:R0、R1、R2和R3。

  • 从R0到R3,特权能力由高到低。
  • R0相当于内核态;R3相当于用户态;R1和R2则介于两者之间。
  • 不同级别能够运行的指令集合不同。

5.CPU状态之间的转换

有了不同的特权级别,那么就需要让用户程序操作系统之间能有转换。

用户态——> 内核态中断/异常/陷入机制

内核态 ——> 用户态:设置程序状态字PSW

实际上用户程序在执行的过程中如果需要操作系统的服务,它就要从用户态能够陷入,进入内核态。而从用户态进入内核态的一个唯一的途径就是中断/异常/陷入机制 。这也是下一个非常重要的主题。

而从内核态返回到用户态则比较简单啊,只是通过设置程序状态字寄存器就可以了。

上面所提到的特殊的指令:陷入指令(访管指令)。它的作用是提供给用户程序的一个接口,用这个接口使用户程序可以向操作系统提出各种服务请求。

之所以叫访管指令, 是因为有的时候内核态也被称为一个supervisor mode管理态。 所以在这种情况下,从用户态进入了管理态,相当于访问管理态,所以叫访管指令。

而这条特殊的指令在不同的计算机系统当中实际上是用不同的指令的。 比如 int 指令,trap 指令,syscall,sysenter/sysexit。 这些指令都是不同的计算机体系结构提供的这种特殊的指令,用于陷入(用于访管)。所以这是关于CPU状态之间的转换需要的不同的条件。


三.中断与异常机制

操作系统当中的中断和异常机制就好比是汽车中的发动机,或者是飞机引擎。靠它来驱动操作系统的运行。 有的时候是可以这么说,操作系统是由中断驱动/事件驱动的。有了这个机制,操作系统就可以做很多的事情。

主要作用:

  • 及时处理设备发来的中断请求
  • 可使OS捕获用户程序提出的服务请求
  • 防止用户程序执行过程中的破坏性活动
  • ……

1.中断与异常的概念

所谓中断与异常,实际上是 CPU 对系统发生的某个事件的一种反应。当这个事件发生的时候,通过对这个事件的处理实际上是改变了一个控制流。我们通常说事件的发生改变了 CPU 的一个控制流。

过程:

  • CPU暂停正在执行的程序,保留现场后自动转去执行相应事件的处理程序
  • 处理完成后返回断点, 继续执行被打断的程序

特点:

  • 是随机发生的
  • 是自动处理的
  • 是可恢复的

2.引入中断与异常的原因

中断

原因:中断的引入实际上是为了支持CPU与外部设备的一个并行操作

早期的计算机系统,如果没有中断机制的话 那么 CPU 要负责对设备的所有工作的管理。但是有了中断机制之后就可以这样来工作了。

比如:CPU 会去启动 输入输出设备的工作。 启动做完了之后设备本身就可以独立自己进行工作了 。而 CPU 这个时候呢可以转去处理一些和这次输入输出没有关系的事情。当设备完成了它的任务之后,它通过通过了中断向 CPU 报告这次输入输出的结果。让 CPU 来决策下面该做什么事情。

异常

原因:表示CPU执行指令时本身出现的问题

如算术溢出、除零、取数时的奇偶错,访存地址时越界或执行了“陷入指令” 等,这时硬件改变了CPU当前的执行流程,转到相应的错误处理程序或异常处理程序或执行系统调用。

3.事件

中断是外部事件在CPU 之外产生的事件打断了 CPU 。这些事件是正在运行的程序所不期望的。

异常是由正在执行的指令而引发的 。

4.小结

四.中断与异常机制的工作原理

中断/异常机制实际上是现代计算机系统中的核心机制之一。它的主要工作是硬件和软件相互配合来完成的通过软硬件的配合,来使计算机的能力得到充分地发挥。中断/异常机制的工作原理主要是从硬件与软件两个方面分析。

硬件:捕获中断源发出的各种中断/异常的请求 ,以某一种方式来响应,通过把控制权转交给特定的处理程序来完成这个过程,而这个过程就是中断 。异常的响应,响应中断,响应异常这是硬件完成的工作。

软件:识别中断, 异常类型完成对应的处理,实际上就是处理程序,把它称之为中断/异常处理程序。

1.中断响应

中断响应是发现中断、接收中断的过程,这部分由中断硬件部件完成。处理器控制部件中设有中断寄存器,其中保存了各种中断信号。

CPU响应中断过程示意:

中断向量表是个非常重要的软硬件结合的这么一个数据结构。 每一行呢实际上是一个中断向量 。中断向量表是由若干中断向量组成。

每一个中断向量其实就是一个内存单元。它是存放了中断处理程序的这个入口地址,以及这个程序,在运行的时候所需要的一个处理机的状态字。

中断响应示意:

2.中断处理程序

设计操作系统时,为每一类中断/异常事件编好相 应的处理程序,并设置好中断向量表。系统运行时若响应中断,中断硬件部件将CPU控 制权转给中断处理程序。

中断处理程序主要做的工作:

  • 保存相关寄存器信息(硬件部件会保存关键寄存器的信息。其他的一些寄存器的信息还要再做进一步地保存)
  • 分析中断/异常的具体原因
  • 执行对应的处理功能
  • 恢复现场,返回被事件打断的程序

3.中断异常机制小节

以设备输入输出中断为例:

  • 打印机给CPU发中断信号
  • CPU处理完当前指令后检测到中断,判断出中断来源并向相关设备发确认信号
  • CPU开始为软件处理中断做准备:
    • 处理器状态被切换到内核态
    • 在系统栈中保存被中断程序的重要上下文环境,主要是程序计数器PC、程 序状态字PSW
  • CPU根据中断码查中断向量表,获得与该中断相关的处理程序的入口地址,并将PC设置成该地址,新的指令周期开始时,CPU控制转移到中断处理程序
  • 中断处理程序开始工作:(软件工作)
    • 在系统栈中保存现场信息
    • 检查I/O设备的状态信息,操纵I/O设备或者在设备和内存之间传送数据等等
  • 中断处理结束时,CPU检测到中断返回指令,从系统 栈中恢复被中断程序的上下文环境 ,CPU状态恢复成 原来的状态,PSW和PC恢复成中断前的值,CPU开始一个新的指令周期。

五.系统调用机制

1.系统调用

什么是系统调用

系统调用是用户在编程时可以调用的操作系统功能。系统调用全称应该是操作系统功能调用,简称系统调用 。

作用

系统调用是操作系统给编程人员提供的唯一接口。 通过系统调用使得 CPU 的状态从用户态陷入了内核态。

2.系统调用,库函数,API,内核函数之间的关系

  • 应用程序可以直接调系统调用。但是通常情况下,应用程序都是通过了 C 函数库或者是 API 的接口来间接地调用系统调用。
  • 在操作系统内核当中,提供了很多的内核函数。这些内核函数经过了封装把它提供到了 C 函数库,或者是 API 接口 。所以系统调用对于内核而言,内核函数就是这个系统调用的处理程序,而这些处理程序通过封装在 C 函数库或者 API 接口呢提供给用户来使用。
  • 但是 C 函数库里头或者是 API 接口里头还有一些函数不是系统调用,它们就是一些普通的函数在完成一些功能。
  • 有一些函数通过系统调用对应到了多个内核函数,也可能是某一个函数通过系统调用对应内核的一个函数。都是不太一样的。
  • 内核函数当中 也有一些函数呢是不开放给用户使用的

应用程序大部分情况下是通过调用函数。 函数执行过程中再去变成系统调用来进入内核来完成。

3.系统调用机制的设计

  1. 首先利用硬件给我们提供的支持(就是中断异常机制)。通过这个机制实现系统调用服务。

  2. 然后选择一条特殊的指令即陷入指令,也称之为访管指令 通过这条指令的执行引发一个异常完成从用户态到内核态的切换工作。看到只有一条指令。也就是说所有的系统调用,都是通过这条指令来进入内核的。

    在中断向量表或中断描述符表当中有一行专门用于系统调用。

  3. 然后操作系统呢要为每一个系统调用事先确定一个编号(系统调用号),因为要确定是哪一个系统调用。所以要通过编号来区分。而且每个系统调用其实还有不同的参数 ,所以呢我们还要设计相应的参数。这些工作除了操作系统的设计之外,还需要编译器来帮忙,编译器会把这个封装的系统调用把它展开 ,在过程中生成这条特殊的陷入指令,以及这些参数的推送寄存器的这些指令。

  4. 每一个系统调用其实都有一段内核函数,或者是一段代码来对应。 找到对应的内核函数就需要设计一张系统调用表,这张表就把系统调用的各项服务的入口地址填在这张表里头,那这张表也是在这个初始化的时候设置好了。

4.参数传递过程问题

实现用户程序的参数传递给内核常用以下3种实现方法:

  • 由陷入指令自带参数 : 陷入指令的长度有限,且 还要携带系统调用功能号,只能自带有限的参数
  • 通过通用寄存器传递参数 : 这些寄存器是操作系 统和用户程序都能访问的,但寄存器的个数会限制传递参数的数量
  • 在内存中开辟专用堆栈区来传递参数

5.系统调用的执行过程

当CPU执行到特殊的陷入指令时:

  • 中断/异常机制 :硬件保护现场;通过查中断向量表把控制权转给系统调用总入口程序
  • 系统调用总入口程序 :保存现场;将参数保存在内核堆 栈里;通过查系统调用表把控制权转给相应的系统调用 处理例程或内核函数
  • 执行系统调用例程
  • 恢复现场,返回用户程序
0%