目录

操作系统教程

目录

前言

  • 致力于传统操作系统基本概念、基本技术、基本方法的阐述
  • 把操作系统成熟的基本原理与当代具有代表性的具体实例紧密地结合起来

涉及内容

操作系统概论

介绍操作系统的基本概念、多道程序设计技术、操作系统的形成和发展,操作系统的分类;操作系统的服务、操作系统的功能、操作系统的接口;操作系统的结构

处理机管理

从处理器和中断技术开始,介绍了中断的概念、分类、处理、优先级和多重中断。接着,引入进程和线程的概念,介绍进程管理的实现模型、线程不同级别的实现方法,介绍处理机调度的三个层次,着重讨论了各种单处理机调度算法,也涉及到多处理机调度算法和实时调度算法

并发进程

介绍进程的顺序性和并发性,进程的协作和竞争,以进程交互、进程控制、进程通信和进程死锁问题为重点,讨论并发程序设计有关技术和各种进程互斥、同步、通信机制和工具

存储管理

讨论存储管理的基本功能、各种传统存储管理技术、虚拟存储管理技术和最新的存储管理技术,如多级页表、反置页表等

设备管理

讨论 I/O 硬件原理、I/O 控制方式、I/O 软件原理、I/O 缓冲技术,着重介绍磁盘驱动调度技术、RAID 技术以及设备分配/去配和虚拟设备技术。也介绍了具有通道的I/O系统管理

文件管理

讨论文件概念、文件目录、文件逻辑结构、文件物理结构、文件的保护和保密、文件存储空间管理以及文件的操作和使用原理。也讨论了文件系统的新概念:内存映射文件和虚拟文件系统。

操作系统安全性

讨论操作系统安全威胁和类型;操作系统保护的层次及保护的基本机制、策略和模型,其中着重讨论了身份认证机制、授权机制、加密机制和审计机制

网络和分布式操作系统

简要介绍网络和分布式操作系统的基本概念和技术,包括网络和数据通信基础、网络体系结构、网络操作系统;分布式进程通信、分布式资源管理、分布式进程同步、分布式文件系统和进程迁移等

操作系统概论

操作系统定义

操作系统是管理系统资源、控制程序执行、改善人机界面、提供各种服务,合理组织计算机工作流程和为用户有效使用计算机提供良好运行环境的一种系统软件。

配置操作系统的主要目标

方便用户使用

OS 通过提供用户与计算机之间的友善接口来方便用户使用

扩大机器功能

OS 通过扩充改造硬件设施和提供新的服务来扩大机器功能

管理系统资源

OS 有效管理好系统中所有硬件软件资源,使之得到充分利用

提高系统效率

OS 合理组织好计算机的工作流程,以改进系统性能和提高系统效率

构筑开放环境

OS 遵循有关国际标准来设计和构造,以构筑出一个开放环境。含义主要是指:遵循有关国际标准(如开放的通信标准、开放的用户接口标准、开放的线程库标准等);支持体系结构的可伸缩性和可扩展性;支持应用程序在不同平台上的可移植性和可互操作性

计算机系统包括硬件和软件两个组成部分。硬件是所有软件运行的物质基础,软件能充分发挥硬件潜能和扩充硬件功能,完成各种系统及应用任务,两者互相促进、相辅相成、缺一不可。

操作系统的作用和功能

操作系统是用户与计算机硬件之间的接口

操作系统是计算机系统的资源管理者

处理器管理
  1. 处理器管理的第一项工作是处理中断事件。硬件只能发现中断事件,捕捉它并产生中断信号,但不能进行处理,配置了操作系统,就能对中断事件进行处理
  2. 处理器管理的第二项工作是处理器调度。处理器是计算机系统中一种稀有和宝贵的资源,应该最大限度地提高处理器的利用率。为了提高处理器的利用率,操作系统采用了多道程序设计技术。在多道程序或多用户的情况下,组织多个作业或任务执行时,就要解决处理器的调度、分配和回收等问题。为了实现处理器管理的功能,描述多道程序的并发执行,操作系统引入了进程的概念,处理器的分配和执行都是以进程为基本单位;随着并行处理技术的发展,为了进一步提高系统并行性,使并发执行单位的粒度变细,并发执行的代价降低,操作系统又引入了线程的概念。对处理器的管理和调度最终归结为对进程和线程的管理和调度,包括:(1)进程控制和管理;(2)进程同步和互斥;(3)进程通信;(4)进程死锁;(5)线程控制和管理;(6)处理器调度,又分高级调度,中级调度和低级调度。 正是由于操作系统对处理器的管理策略不同,其提供的作业处理方式也就不同,例如,批处理方式、分时处理方式、实时处理方式等等。从而,呈现在用户面前,成为具有不同处理方式和不同特点的操作系统。
存储管理

存储管理的主要任务是管理存储器资源,为多道程序运行提供有力的支撑,便于用户使用存储资源,提高存储空间的利用率。存储管理的主要功能包括:

  1. 存储分配。存储管理将根据用户程序的需要分配给它存储器资源,这是多道程序能并发执行的首要条件,当然程序运行结束时,还需回收存储资源。
  2. 存储共享。存储管理能让内存储器(又叫主存储器)中的多个用户程序实现存储资源的共享,以提高存储器的利用率。
  3. 地址转换与存储保护。存储管理负责把用户的逻辑地址转换成物理地址,同时要保证各个用户程序相互隔离起来互不干扰,更不允许用户程序访问操作系统的程序和数据,从而,保护系统和用户程序存放在存储器中的信息不被破坏。
  4. 存储扩充。由于受到处理器寻址能力的限制,一台计算机的物理内存容量总是有限的,难以满足用户大型程序的需求,而外存储器容量大且价格便宜。存储管理还应该能从逻辑上来扩充内存储器,把内存和外存混合起来使用,为用户提供一个比内存实际容量大得多的逻辑编程空间,方便用户的编程和使用。
设备管理

设备管理的主要任务是管理各类外围设备,完成用户提出的I/O请求,加快I/O信息的传送速度,发挥I/O设备的并行性,提高I/O设备的利用率,以及提供每种设备的设备驱动程序和中断处理程序,为用户隐蔽硬件细节,提供方便简单的设备使用方法。为实现这些任务,设备管理应该具有以下功能:

  1. 提供外围设备的控制与处理
  2. 提供缓冲区的管理
  3. 提供设备独立性
  4. 外围设备的分配和去配
  5. 实现共享型外围设备的驱动调度
  6. 实现虚拟设备
文件管理

文件管理则是针对系统中的信息资源的。在现代计算机中,通常把程序和数据以文件形式存储在外存储器(又叫辅存储器)上,供用户使用,这样,外存储器上保存了大量文件。 文件管理,它的主要任务是对用户文件和系统文件进行有效管理,实现按名存取;实现文件的共享、保护和保密,保证文件的安全性;并提供给用户一整套能方便使用文件的操作和命令。 具体来说,文件管理要完成以下任务:

  1. 提供文件的逻辑组织方法
  2. 提供文件的物理组织方法
  3. 提供文件的存取方法
  4. 提供文件的使用方法
  5. 实现文件的目录管理
  6. 实现文件的共享和存取控制
  7. 实现文件的存储空间管理
网络与通信管理

计算机网络源于计算机与通信技术的结合,联网操作系统至少应具有以下管理功能:

  1. 网上资源管理功能。计算机网络的主要目的之一是共享资源,网络操作系统应实现网上资源的共享,管理用户应用程序对资源的访问,保证信息资源的安全性和完整性。
  2. 数据通信管理功能。计算机联网后,结点之间可以互相传送数据,进行通信,通过通信软件,按照通信协议的规定,完成网络上计算机之间的信息传送。
  3. 网络管理功能。包括故障管理、安全管理、性能管理、记帐管理和配置管理等。
用户接口

为了使用户能灵活、方便地使用计算机和系统功能,操作系统还提供了一组友好的使用其功能的手段称用户接口,它包括两大类:程序接口和操作接口。用户通过这些接口能方便地调用操作系统功能,有效地组织作业及其工作和处理流程,并使整个系统能高效地运行。

操作系统的主要特性

并发性

并发性是指两个或两个以上的事件或活动在同一时间间隔内发生。操作系统的并发性是指计算机系统中同时存在若干个运行着的程序,因此,它应该具有处理和调度多个程序同时执行的能力。

发挥并发性能够消除计算机系统中部件和部件之间的相互等待,有效地改善系统资源的利用率,改进系统的吞吐率,提高系统效率。

并发性虽然能有效改善系统资源的利用率,但却会引发一系列的问题,使操作系统的设计和实现变得复杂化。如:进程切换、进程调度、进程隔离保护、进程协作

在多处理器系统中,程序的并发性不仅体现在宏观上,而且体现在微观上,这称为并行的。并行性是指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同 CPU 上同时执行。而在分布式系统中,多台计算机的并存使程序的并发性得到了更充分的发挥,因为,同一时刻每台计算机上都可以有程序在执行。可见并行的事件或活动一定是并发的,但反之并发的事件或活动未必是并行的,并行性是并发性的特例,而并发性是并行性的扩展。由于并发技术的本质思想是:当一个程序发生事件(如等待 I/O)时出让其占用的 CPU 而由另一个程序运行,据此不难看出,实现并发技术的关键之一是如何对系统内的多个运行程序(进程)进行切换的技术。

共享性

共享指计算机系统中的资源(包括硬件资源和信息资源)可被多个并发执行的用户程序和系统程序共同使用,而不是被其中某一个程序所独占。

资源共享的方式可以分成两种:

  1. 互斥访问 系统中的某些资源如打印机、磁带机、卡片机,虽然它们可提供给多个程序使用,但在同一时间段内却只允许一个程序访问这些资源,即要求互相排斥地使用这些资源
  2. 同时访问 允许同一时间内多个程序对它们进行访问,这里“同时”是宏观上的说法,从微观上看多个程序访问资源仍然是交错的,只是这种交错访问的顺序对访问的结果没有影响罢了。典型的可供多个程序同时访问的资源是磁盘,各种可重入程序也可被同时访问

与共享性有关的问题是资源分配、信息保护、存取控制等,必须要妥善解决好这些问题。

共享性和并发性是操作系统两个最基本的特性,它们互为依存。一方面,资源的共享是因为程序的并发执行而引起的,若系统不允许程序并发执行,自然也就不存在资源共享问题。另一方面,若系统不能对资源共享实施有效管理,必然会影响到程序的并发执行,甚至程序无法并发执行,操作系统也就失去了并发性,导致整个系统效率低下。

异步性

在多道程序环境中,允许多个进程并发执行,由于资源有限而进程众多,多数情况下,进程的执行不是一贯到底,而是“走走停停”。

虚拟性

虚拟性是指操作系统中的一种管理技术,它是把物理上的一个实体变成逻辑上的多个对应物,或把物理上的多个实体变成逻辑上的一个对应物的技术。显然,前者是实际存在的而后者是虚构假想的,采用虚拟技术的目的是为用户提供易于使用、方便高效的操作环境。比如:1个 CPU 被虚拟成两个、1台机器可以虚拟出多台的硬件

操作系统的形成和发展

人工操作阶段

初始操作系统尚未出现,由程序员采用手工方式直接控制和使用计算机硬件,程序员使用机器语言编程,并将事先准备好的程序和数据穿孔在纸带或卡片上,从纸带或卡片输入机将程序和数据输入计算机。然后,启动计算机运行程序,程序员可以通过控制台上的按钮、开关和氖灯来操纵和控制程序,运行完毕,取走计算输出的结果,才轮到下一个用户上机。

后来,汇编语言产生了,在汇编系统中,数字操作码被记忆码代替,程序按固定格式的汇编语言书写。系统程序员预先编制一个汇编程序,它把用汇编语言书写的“源程序”解释成计算机能直接执行的机器语言格式的目标程序。随后,一些高级程序设计语言相继出现进一步方便了编程。

执行时需要把汇编程序或编译系统以及源程序和数据,都穿在卡片或纸带上,然后,再装入和执行。 其大致过程为:

  1. 人工把源程序用穿孔机穿在卡片或纸带上
  2. 将准备好的汇编程序或编译系统装入计算机
  3. 汇编程序或编译系统读入人工装在输入机上的穿孔卡片或穿孔带上的源程序
  4. 执行汇编过程或编译过程,产生目标程序,并输出到目标卡片迭或纸带
  5. 通过引导程序把装在输入机上的目标程序读入计算机
  6. 执行目标程序,从输入机上读入人工装好的数据卡片或数据带上的数据
  7. 产生计算结果,把执行结果从打印机上或卡片机上输出 上述方式比直接用机器语言前进了一步,程序易于编制和读取,汇编程序或编译系统可执行存储、分配等辅助工作,从而,在一定程度上减轻了用户的负担。

人工操作方式存在严重缺点:

  1. 用户独占资源
  2. 人工干预较多
  3. 计算时间拉长

这种人工操作方式在慢速的计算机上还能容忍,但是随着计算机速度的提高,其缺点就更加暴露出来了。

此外,随着 CPU 速度迅速提高而 I/O 设备速度却提高不多,导致 CPU 与 I/O 设备之间的速度不匹配,矛盾越来越突出,需要妥善解决这些问题

管理程序阶段

早期批处理系统借助于作业控制语言变革了计算机的手工操作方式。用户不再通过开关和按钮来控制计算机的执行,而是通过脱机方式使用计算机,通过作业控制卡来描述对作业的加工和控制步骤,并把作业控制卡连同程序、数据一起提交给计算机的操作员,操作员收集到一批作业后一起把它们放到卡片机上输入计算机。计算机上则运行一个驻留在内存中的执行程序,以对作业进行自动控制和成批处理,自动进行作业转换以减少系统空闲和手工操作时间。其工作流程如下:执行程序将一批作业从纸带或卡片机输入到磁带上,每当一批作业输入完成后,执行程序自动把磁带上的第一个作业装入内存,并把控制权交给作业。当该作业执行完成后,执行程序收回控制权并再调入磁带上的第二个作业到内存执行。计算机在执行程序的控制下就这样连续地一个作业一个作业地执行,直至磁带上的作业全部做完。这种系统能实现作业到作业的自动转换,缩短作业的准备和建立时间,减少人工操作和干预,让计算机尽可能地连续运转。

早期的批处理系统中,一开始作业的输入和输出均是联机的,联机 I/O 的缺点是速度慢, I/O 设备和 CPU 仍然串行工作,CPU 时间浪费很大,为此,在批处理中引进了脱机 I/O 技术。除主机外,另设一台辅机,该机仅与 I/O 设备打交道,不与主机连接。输入设备上的作业通过辅机输到磁带上,这叫脱机输入;主机负责从磁带上把作业读入内存执行,作业完成后,主机负责把结果输出到磁带上,这叫脱机输出;然后,由辅机把磁带上的结果信息在打印机上打印输出。这称作脱机 I/O 技术。

为了发挥批处理系统的性能,缩短作业的准备和建立时间,驻留在内存工作的执行程序的功能得到了很大的扩充,进化到管理程序。FMS 和 IBSYS )是这类系统的典型实例。 管理程序的内存组织如图所示,它的主要功能小结如下:

管理程序的内存组织
- 自动控制和处理作业流。管理程序把控制权传送给一个作业,当作业运行结束时,它又收回控制权,继续调度下一个作业执行,自动控制和处理作业流,减少了作业的准备和建立时间。作业流的自动控制和处理依靠作业控制语言,因而,促进了作业控制语言的发展。作业控制语言是由一些描述作业控制过程的语句组成的,每个语句附有一行作业或作业步信息编码,并以穿孔卡的形式提供。例如,$\$JOB$ 卡表示启动一个新作业; $\$FIN$ 卡表示调用 FORTRAN 编译系统; $\$ASM$ 卡表示调用汇编程序;$\$LOAD$ 卡表示调用装配程序;$\$DATA$ 卡指定数据;$\$RUN$ 卡执行用户程序;$\$END$ 卡表示一个作业结束。管理程序通过输入、解释并执行嵌入用户作业的作业控制卡规定的功能,就能自动地处理用户作业流。每个作业完成后,管理程序又自动地从输入机上读取下一个作业运行,直到整批作业处理结束。 - 提供一套操作命令。操作员通过打字机输入命令,管理程序识别并执行命令,这样不仅速度快,操作员还可进行一些复杂的控制。输出信息也可由打字机输出,代替了早期的氖灯显示,易于理解。这种交互方式不仅提高了效率,也便于使用 - 提供设备驱动和 I/O 控制功能。系统提供标准 I/O 程序,用户通过管理程序获得和使用 I/O 设备,减轻了用户驱动物理设备的负担。管理程序还能处理某些特殊设备和设备故障,改进了设备的可靠性和可用性。 - 提供库程序和程序装配功能。库程序包括:汇编程序、FORTRAN 语言编译程序、标准 I/O 程序、标准子程序等。通常,用户程序必须调用库程序才能执行下去,装配工作由管理程序完成。所有程序都按相对地址编址,管理程序把相应库程序和用户程序进行装配,并转换成绝对地址形式的目标程序,以便执行。 - 提供简单的文件管理功能。用户通过输入设备输入程序和数据,为了反复使用,用户希望能把这些信息保存起来,以便随时使用,这就产生了文件系统。从此,用户可按文件名字,而不是信息的物理地址进行存取,方便灵活,安全可靠。

多道程序设计与操作系统的形成

多道程序设计

在早期的单道批处理系统中,内存中仅有单个作业在运行,致使系统中仍有许多资源空闲,设备利用率低,系统性能较差。

单道算题运行时处理器的使用效率
当 CPU 工作时,外部设备不能工作;而外部设备工作时,CPU 必须等待。

20 世纪 60 年代初,有两项技术取得了突破:中断和通道,这两种技术结合起来为实现 CPU 和 I/O 设备的并行工作提供了基础,这时,多道程序的概念才变成了现实。 多道程序设计是指允许多个程序(作业)同时进入一个计算机系统的内存储器并启动进行交替计算的方法。也就是说,计算机内存中同时存放了多道(二个以上相互独立的)程序,它们均处于开始和结束点之间。从宏观上看是并行的,多道程序都处于运行过程中,但都未运行结束;从微观上看是串行的,各道程序轮流占用 CPU,交替地执行。引入多道程序设计技术的根本目的是提高 CPU 的利用率,充分发挥计算机系统部件的并行性,现代计算机系统都采用了多道程序设计技术。

操作系统的形成

第三代计算机的性能有了更大提高,机器速度更快,内外存容量增大,I/O 设备数量和种类增多,为软件的发展提供了有力支持。如何更好地发挥硬件功效,如何更好地满足各种应用的需要,这些都迫切要求扩充管理程序的功能。

中断技术通道技术 的出现使得硬部件具有了较强的并行工作能力,从理论上来说,实现多道程序系统已无问题。但是,从半自动的管理程序方式过渡到能够自动控制程序执行的操作系统方式,对辅助存储器性能的要求增高。这个阶段虽然有个别的磁带操作系统出现,但操作系统的真正形成还期待着大容量高速辅助存储器的出现。

大约到 60 年代中期以后,随着磁盘的问世,相继出现了多道批处理操作系统和分时操作系统、实时操作系统,到这个时候标志着操作系统正式形成。

计算机配置操作系统后,其资源管理水平和操作自动化程度有了进一步提高,具体表现在:

  • 操作系统实现了计算机操作过程的自动化。批处理方式更为完善和方便,作业控制语言有了进一步发展,为优化调度和管理控制提供了新手段
  • 资源管理水平有了提高,实现了外围设备的联机同时操作, 进一步提高了计算机资源的利用率
  • 提供虚存管理功能,由于多个用户作业同时在内存中运行,在硬件设施的支持下,操作系统为多个用户作业提供了存储分配、共享、保护和扩充的功能,导致操作系统步入实用化
  • 支持分时操作,多个用户通过终端可以同时联机地与一个计算机系统交互
  • 文件管理功能有改进,数据库系统开始出现
  • 多道程序设计趋于完善,采用复杂的调度算法,充分利用各类资源,最大限度地提高计算机系统效率
操作系统的发展和分类

促使操作系统不断发展的主要动力有以下五个方面:

  1. 器件快速更新换代:CPU 性能快速提升
  2. 计算机体系结构不断发展:硬件的改进促使计算机技术的进步
  3. 提高计算机系统资源利用率的需要
  4. 让用户使用计算机越来越方便的需要
  5. 满足用户的新要求,提供给用户新服务

从操作系统形成以来,按照功能、特点和使用方式的不同,可把操作系统区分为三种基本类型:

  1. 批处理操作系统,特征如下:

    1. 用户脱机工作
    2. 成批处理作业
    3. 单/多个程序运行
  2. 分时操作系统,特征如下:

    1. 同时性。若干个终端用户同时联机使用计算机,分时就是指多个用户分享使用同一台计算机的 CPU 时间
    2. 独立性。终端用户彼此独立,互不干扰,每个终端用户感觉上好像他独占了这台计算机
    3. 及时性。终端用户的立即型请求(即不要求大量 CPU 时间处理的请求)能在足够快的时间之内得到响应(通常应该为 2 ~ 3 秒钟)。这一特性与计算机 CPU 的处理速度、分时系统中联机终端用户数目和时间片的长短密切相关
    4. 交互性。人机交互,联机工作,用户直接控制其程序的运行,便于程序的调试和排错。

    分时操作系统和批处理操作系统虽然有共性,它们都基于多道程序设计技术,但存在下列不同点:

    1. 追求的目标不同。批处理系统以提高系统资源利用率和作业吞吐率为目标;分时系统则要满足多个联机用户立即型命令的快速响应。
    2. 适应的作业不同。批处理适应已经调试好的大型作业;而分时系统适应正在调试的小作业。
    3. 资源的利用率不同。批处理操作系统可合理安排不同负载的作业,使各种资源利用率较佳;分时操作系统中,多个终端作业使用相同类型编译系统、运行系统和公共子程序时,系统调用它们的开销较小。
    4. 作业控制的方式不同。批处理由用户通过 JCL 的语句书写作业控制流,预先提交,脱机工作;交互型作业,由用户从键盘输入操作命令控制,交互方式,联机工作。
  3. 实时操作系统;目前有三种典型的实时系统,过程控制系统、信息查询系统和事务处理系统。

  4. 微机操作系统;供个人使用

  5. 并行操作系统;计算机的应用经历了从数据处理到信息处理,从信息处理到知识处理,每前进一步都要求增加计算机的处理能力。

  6. 网络操作系统;计算机网络是通过通信设施将地理上分散的并具有自治功能的多个计算机系统互连起来的系统

  7. 分布式操作系统

操作系统提供的服务和用户接口

操作系统提供的基本服务

操作系统要为用户程序的执行提供一个良好的运行环境,它要为程序及其用户提供各种服务,当然不同的操作系统提供的服务不完全相同,但有许多共同点。

提供操作系统共性服务为程序员带来了方便,使编程任务变得更加容易,操作系统提供给程序和用户的共性服务大致有:

  1. 创建程序。提供各种工具和服务,程序的编辑工具和调试工具,帮助用户编程并生成高质量的源程序等服务
  2. 执行程序。将用户程序和数据装入主存,为其运行做好一切准备工作并启动和执行程序。当程序编译或运行出现异常时,应能报告发生的情况,终止程序执行或进行适当处理
  3. 数据I/O
  4. 信息存取。文件系统让用户按文件名来建立、读写、修改以及删除文件,使用方便,安全可靠。当涉及多用户访问或共享文件时,操作系统将提供信息保护机制
  5. 通信服务。在许多情况下,一个进程要与另外的进程交换信息,这种通信一般分为两种情况,一是在同一台计算机上执行的进程之间通信;二是在被网络连接在一起的不同计算机上执行的进程之间通信
  6. 错误检测和处理。操作系统能捕捉和处理各种硬件或软件造成的差错或异常,并让这些差错或异常造成的影响缩小在最小范围内,必要时及时报告给操作员或用户。

此外,操作系统除上述提供给用户的服务外,还具有另外一些功能,以保证它自身高效率、高质量地工作,从而,使得多个用户程序能够有效地共享系统资源,提高系统效率,这些功能有:

  1. 资源分配
  2. 统计
  3. 保护
操作系统提供的用户接口

操作系统可以通过程序接口和操作接口两种方式把它的服务和功能提供给用户,反过来也可以这样说,用户可以通过两个接口来调用操作系统提供的服务和功能

用户和操作系统间的两种接口
程序接口与系统调用
  1. 系统调用

  2. 系统调用的实现要点

  3. 系统调用与过程(函数)调用的区别

    程序中执行系统调用或过程(函数)调用,虽然都是对某种功能或服务的需求,但两者从调用形式到具体实现都有很大区别。

    1. 调用形式不同。过程(函数)使用一般调用指令,其转向地址是固定不变的,包含在跳转语句中,但系统调用中不包含处理程序入口,而仅仅提供功能号,按功能号调用
    2. 被调用代码的位置不同。过程(函数)调用是一种静态调用,调用程序和被调用代码在同一程序内,经过连接编辑后作为目标代码的一部份。当过程(函数)升级或修改时,必须重新编译连接。而系统调用是一种动态调用,系统调用的处理代码在调用程序之外(在操作系统中),这样一来,系统调用处理代码升级或修改时,与调用程序无关。而且,调用程序的长度也大大缩短,减少了调用程序占用的存储空间。
    3. 提供方式不同。过程(函数)往往由编译系统提供,不同编译系统提供的过程(函数)可以不同;系统调用由操作系统提供,一旦操作系统设计好,系统调用的功能、种类与数量便固定不变了。
    4. 调用的实现不同。程序使用一般机器指令(跳转指令)来调用过程(函数),是在用户态运行的;程序执行系统调用,是通过中断机构来实现,需要从用户态转变到核心态,在管理状态执行。因此程序执行系统调用安全性好
  4. Linux 的系统调用

    每个系统调用由两部分组成:

    1. 核心函数:是实现系统调用功能的(内核)代码,它运行在核心态,数据也存放在内核空间,通常它不能再使用系统调用,也不能使用应用程序可用的库函数。
    2. 接口函数:是提供给应用程序的 API,以库函数形式存在,该库中存放了所有系统调用的接口函数的目标代码,用汇编语言书写。其主要功能是把系统调用号、入口参数地址传送给相应的核心函数,并使用户态下运行的应用程序陷入核心态(Linux内核陷入由 0X80 中断实现,调用流程:1. 取系统调用号,检验合法性;2. 建立调用堆栈,保护现场信息;3. 根据系统调用号定位核心函数地址;4. 根据通用寄存器内容,从用户栈中取入口参数;5. 核心函数执行,把结果返回应用程序; 6. 执行退栈操作,判别调度程序 scheduler 是否要被执行)。
操作接口与系统程序
  1. 作业控制方式
  2. 命令解释程序
  3. 系统程序

操作系统的结构设计

操作系统的构件
  1. 内核
  2. 进程
  3. 线程
  4. 管程
  5. 类程
整体式结构的操作系统
层次式结构的操作系统
虚拟机结构的操作系统
客户/服务器与微内核结构的操作系统
  1. 客户/服务器与微内核结构
  2. 微内核的设计
操作系统的运行模型
  1. 非进程内核模型
  2. OS 功能(函数)在用户进程内执行的模型
  3. OS 功能(函数)作为独立进程执行的模型

总结

处理器管理

处理器管理是操作系统的重要组成部分,它负责管理、调度和分派计算机系统的重要资源———处理器,并控制程序的执行。由于处理器管理是操作系统中最核心的组成部分,任何程序的执行都必须真正占有处理器,因此,处理器管理直接影响系统的性能。

操作系统的基本任务是对“进程”实施管理,操作系统必须有效控制进程的执行、给进程分配资源、允许进程之间共享和交换信息、保护每个进程在运行期间免受其他进程干扰、控制进程的互斥、同步和通信。为达到这些要求,操作系统的处理器管理必须为每一个进程维护一个数据结构,用以描述该进程的状态和分配到的资源,并允许操作系统行使对进程的控制权。

中央处理器

计算机系统的核心是中央处理器。如果一个计算机系统只包括一个运算处理器,称之为单处理器系统。如果有多个运算处理器,则称之为多处理器系统。

早期的计算机系统是基于单个处理器的顺序处理机器,每一条指令的执行也是串行的,为提高计算机处理的速度,首先发展起来的是联想存储器系统和流水线系统,前者提出了数据驱动的思想,后者解决了指令并行执行的问题,这两者都是最初计算机并行化发展的例子。

随着硬件技术的进步,并行处理技术得到了迅猛的发展,计算机系统不再局限于单处理器和单数据流,各种各样的并行结构得到了应用。

目前计算机系统可以分作以下四类:

  1. 单指令流单数据流(SISD)。一个处理器在一个存储器中的数据上执行单条指令流。
  2. 单指令流多数据流(SIMD)。单条指令流控制多个处理单元同时执行,每个处理单元包括处理器和相关的数据存储,一条指令事实上控制了不同的处理器对不同的数据进行了操作。向量机和阵列机是这类计算机系统的代表。
  3. 多指令流单数据流(MISD)。一个数据流被传送给一组处理器,通过这一组处理器上的不同指令操作最终得到处理结果
  4. 多指令流多数据流(MIMD)。多个处理器对各自不同的数据集同时执行不同的指令流。可以把 MIMD 系统划分为共享内存的紧密耦合 MIMD 系统和内存分布的松散耦合 MIMD 系统两大类。

根据处理器分配策略,紧密耦合 MIMD 系统可以分为主从式系统 MSP 和对称式系统 SMP 两类。

主从式系统的基本思想是:在一个特别的处理器上运行操作系统内核,其他处理器上则运行用户程序和操作系统例行程序,内核负责分配和调度各个处理器,并向其他程序提供各种服务(如输入输出)。这种方式实现简单,但是主处理器的崩溃会导致整个系统的崩溃,并且极可能在主处理器形成性能瓶颈。

在对称式多处理器系统中有两个或两个以上的处理器,操作系统内核可以运行在任意一个处理器上。每个处理器都可以自我调度运行的进程和线程,单个进程的多个线程可在不同处理器上同时运行,服务器进程可以使用多个线程去处理同时来自多个客户的请求,并且操作系统内核也被设计成多进程或多线程,内核的各个部分可以并行执行。

对称多处理器是迄今为止开发出的最为成功的两种并行机之一,有一种 SMP 机最多可支持 64 个处理器,多个处理器之间采用共享主存储器。SMP 机有对称性、单一地址空间、低通信延迟和一致的高速缓存等特点,具有高可靠性、可扩充性、易伸缩性。这一系统中任何处理器都可以访问任何存储单元及 I/O 设备;处理器之间通信代价很低,而并行度较高。

由于共享存储器中只要保存一个操作系统和数据库副本,既有利于动态负载平衡,又有利于保证数据的完整性和一致性。

在松散耦合 MIMD 系统中,每个处理单元都有一个独立的内存储器,各个处理单元之间通过设定的线路或网络通信,多计算机系统和集群系统都是松散耦合 MIMD 系统的例子。

集群系统是迄今为止开发出的另一种最为成功的并行机,它是一组互联的计算机系统。因此也是分布式系统的一种,集群操作系统也是分布式操作系统的一个品种。集群系统运行时构成统一的计算资源,给人以一台机器的感觉。集群系统的配置一般有两种方法,一是各个节点计算机自带磁盘,二是多个节点计算机共享 RAID 磁盘。

在集群系统中,每一台计算机都是一个完整的节点,离开集群后自己可以独立地工作,所以一个节点的失效并不意味着服务的失败,从而使集群系统具备很好的容错性。集群系统还具有很好的可伸缩性,可以用低成本的微机和以太网设备等产品构成。

寄存器

计算机系统的处理器包括一组寄存器,其个数根据机型(处理器型号)的不同而不同,它们构成了一级存储,虽然比主存储器容量要小的多,但是访问速度要快的多。这组寄存器所存储的信息与程序的执行有很大的关系,构成了处理器现场。

不同类型的处理器具有不同的寄存器组成。一般来说,这些寄存器可以分为以下几类:

  1. 通用寄存器。可由程序设计者指定许多功能,如存放操作数或用作寻址寄存器。
  2. 数据寄存器。用以存放操作数。它们作为内存数据的高速缓存,可以被系统程序和用户程序直接使用并进行计算。
  3. 地址寄存器。用于指明内存地址,如索引寄存器、段寄存器(基址/限长)、堆栈指针寄存器等。
  4. I/O 地址寄存器。用于指定 I/O 设备
  5. I/O 缓冲寄存器。用于处理器和 I/O 设备交换数据
  6. 控制寄存器。用于存放处理器的控制和状态信息,它至少应该包括程序计数器 PC 和指令寄存器 IR,中断寄存器以及用于存储器和 I/O 模块控制的寄存器。此外还有存放将被访问的存储单元地址的存储器地址寄存器,以及存放从存储器读出或欲写入的数据的存储器数据寄存器。

特权指令与非特权指令

计算机的基本功能是执行程序,而最终被执行的程序是存储在内存中的机器指令。处理器根据程序计数器(PC)内存中取一条指令到指令寄存器(IR)并执行它,PC 将自动地增长或改变为转移地址以指明下一条要执行的指令的入口地址。

每台计算机的机器指令的集合称指令系统,它反映了一台机器的功能和处理能力,可以分为以下五类:

  1. 数据处理类指令:用于执行算术和逻辑运算
  2. 转移类指令:如无条件转移、条件转移、计数转移等用于改变指令执行序列
  3. 数据传送类指令:用于在处理器的寄存器和寄存器、寄存器和存储器单元、存储器单元和存储器之间交换数据
  4. 移位与字符串指令,移位分算术、逻辑和循环移位。字符串处理有字符串的传送、比较、查询和转换。
  5. I/O类指令:用于启动外围设备,让主存和外围设备之间交换数据

引入操作系统后,操作系统核心程序可以使用全部机器指令,但用户程序只能使用机器指令系统的一个子集。这是因为,用户程序执行一些有关资源管理的机器指令时很容易导致系统混乱,造成系统或用户信息的破坏。

因此,在多道程序设计环境中,从资源管理和控制程序执行的角度出发,必须把指令系统中的指令分作两类:特权指令和非特权指令。

所谓特权指令是指那些只能提供给操作系统的核心程序使用的指令,如启动输入 % 输出设备、设置时钟、控制中断屏蔽位、清内存、建立存储键,加载 PSW 等。只有操作系统才能执行全部指令(特权指令和非特权指令),如果一般用户执行特权指令,会导致非法执行而产生保护中断,转交给操作系统的“用户非法执行特权指令”的特殊处理程序处理。

处理器状态

那么,中央处理器怎么知道当前是操作系统还是一般用户在其上运行呢?这将依赖于处理器状态的标志。在执行不同程序时,根据执行程序对资源和机器指令的使用权限把处理器设置成不同状态。

处理器状态又称为处理器的运行模式,有些系统把处理器状态划分为核心状态、管理状态和用户状态,而大多数系统把处理器状态简单划分为管理状态(又称特权状态、系统模式、特态或管态)和用户状态(又称目标状态、用户模式、常态或目态)。

当处理器处于管理状态时,程序可以执行全部指令,访问所有资源,并具有改变处理器状态的能力;当处理器处于用户状态时,程序只能执行非特权指令。

Intel 奔腾处理器由四种状态,支持 4 个特权级别,0 级权限最高,3 级权限最低。

  • 0 级为操作系统内核级。处理 I/O 、存储管理和其他关键操作。
  • 1 级为系统调用处理程序级。用户程序可以通过调用这里的过程执行系统调用,但是只有一些特定的和受保护的过程可以被调用。
  • 2 级为共享库过程级。它可以被很多正在运行的程序共享,用户程序可以调用这些过程,读取它们的数据,但是不能修改它们。
  • 3 级为用户程序级。它受到的保护最少。

当然,各个操作系统在实现过程中可以根据具体策略有选择地使用硬件提供的保护级别,如运行在 奔腾 上的 windows 操作系统只使用了 0 级 和 3 级。

下面两类情况会导致从用户态向管理状态转换

  1. 程序请求操作系统服务,执行一条系统调用
  2. 程序运行时,产生了一个中断事件,运行程序被中断,让中断处理程序工作

这两类情况都是通过中断机构才发生的,可以说中断是目态到管态转换的惟一途径。当系统中断响应交换程序状态字时,这个处理中断事件的处理程序的程序状态字的处理器状态位标志一定为“管态”。怎样实现管态到目态的转换呢?每台计算机通常会提供一条特权指令称作加载程序状态字 LPSW (Load PSW),用来实现操作系统向用户程序的转换。

程序状态字寄存器

操作系统通过引入程序状态字 PSW (Program Status Word) 来区别不同的处理器工作状态。

,程序状态字用来指示处理器状态、控制指令的执行顺序并且保留和指示与运行程序有关的各种信息,其主要作用是方便地实现程序状态的保护和恢复。每个正在执行的程序都有一个与其执行相关的 PSW ,而每个处理器都设置一个程序状态字寄存器。

一个程序占有处理器执行,它的 PSW 将占有程序状态字寄存器。

一般来说,程序状态字寄存器包括以下几类内容:

  1. 程序基本状态。包括:
    1. 程序计数器:指明下一条执行的指令地址;
    2. 条件码:表示指令执行的结果状态;
    3. 处理器状态位:指明当前的处理器状态,如目态或管态、运行或等待。
  2. 中断码。保存程序执行时当前发生的中断事件。
  3. 中断屏蔽位。指明程序执行中发生中断事件时,是否响应出现的中断事件。

由于不同处理器中的控制寄存器组织方式不同,所以在大多数计算机的处理器现场中可能找不到一个称为程序状态字寄存器的具体寄存器,但总是有一组控制与状态寄存器实际上起到这一作用。

在 Intel 奔腾中,程序状态字由标志寄存器 EFLAGS 和指令指针寄存器 EIP 组成,均为 32 位。EFLAGS 的低 16 位称 FLAGS,可当作一个单元来处理。标志可划分为三组:状态标志、控制标志、系统标志。

  • 状态标志:它使得一条指令的执行结果影响后面的指令。算术运算指令使用 OF(溢出标志)、SF(符号标志)、ZF(结果为0标志)、AF(辅助进位标志)、CF(进位标志)、PF(奇偶校验标志);串扫描、串比较、循环指令使用 ZF 通知其操作结束。
  • 控制标志:有以下几位,DF(方向标志)控制串指令操作,设定DF为1,使得串指令自动减量,即从高地址向低地址处理串操作;DF为0时,串指令自动增量。VM(虚拟86方式标志)为1时,从保护模式进入虚拟8086模式。TF(步进标志)为1时,使处理器执行单步操作。IF(陷阱标志)为1时,允许响应中断,否则关中断。
  • 系统标志:与进程管理有关,共三个IOPL(I/O特权级标志)、NT(嵌套任务标志)和RF(恢复标志),被用于保护模式。指令指令指针寄存器 EIP 的低 16 位被称为 IP(保护模式使用32位),存放下一条顺序执行的指令相对于当前代码段开始地址的一个偏移地址,IP可当作一个单元使用,这在某些情况下是很有用的。

中断技术

现代计算机中都配置了硬件中断装置,中断机制是操作系统的重要组成部分之一。

每当用户程序执行系统调用以求获得系统的服务和帮助、或操作系统管理 I/O 设备和处理形形色色的内部和外部事件时,都需要通过中断机制进行处理。所以,也有人说操作系统是由 “中断驱动” 的。

中断是指程序执行过程中,当发生某个事件时,中止 CPU 上现行程序的运行,引出处理该事件的服务程序执行的过程。

在提供中断装置的计算机系统中,在每两条指令或某些特殊指令执行期间都检查是否有中断事件发生,若无则立即执行下一条或继续执行,否则响应该事件并转去处理中断事件。

中断这种处理突发事件的能力是由硬件和软件协作完成的。首先,由硬件的中断装置发现产生的中断事件,然后,中断装置中止现行程序的执行,引出处理该事件的程序来处理。

计算机系统不仅可以处理由于硬件或软件错误而产生的事件,而且可以处理某种预见要发生的事件。例如,外围设备工作结束时,也发出中断请求,向系统报告它已完成任务,系统根据具体情况做出相应处理。

引起中断的事件称为中断源。发现中断源并产生中断的硬件称中断装置。在不同的硬件结构中,通常有不同的中断源和不同的中断装置,但它们有一个共性,即当中断事件发生后,中断装置能改变处理器内操作执行的顺序,可见中断是现代操作系统实现并发性的基础之一。

中断源分类

从中断事件的性质和激活的手段来说,可以分成强迫性中断事件自愿性中断事件两大类。

强迫性中断事件不是正在运行的程序所期待的,而是由于随机发生的某种事故或外部请求信息所引起的。这类中断事件大致有以下几种:

  • 机器故障中断事件。例如电源故障、主存储器出错等
  • 程序性中断事件。例如定点溢出、除数为0、地址越界等。由于这类中断反映程序执行中发现的例外情况,所以又称异常
  • 外部中断事件。例如时钟的定时中断、控制台发控制信息等
  • 输入输出中断事件。例如设备出错、传输结束等

自愿性中断事件是正在运行的程序所期待的事件。这种事件是由于执行了一条访管指令而引起的,它表示正在运行的程序对操作系统有某种需求,一旦机器执行到一条访管指令时,便自愿停止现行程序的执行而转入访管中断处理程序处理。例如,要求操作系统协助启动外围设备工作。

两类中断事件的响应过程略有不同,详见图:

两类中断事件

还可以按照中断信号的来源,把中断分为外中断和内中断两类:

  • 外中断。一般又称中断,是指来自处理器和主存储器之外的中断,包括电源故障中断、时钟中断、控制台中断、它机中断和 I/O 中断等。每个不同的中断具有不同的中断优先级,在处理高一级中断时,往往会屏蔽部分或全部低级中断。
  • 内中断。是指来自处理器和主存内部的中断,一般又称异常,包括通路校验错、主存奇偶错、非法操作码、地址越界、页面失效、调试指令、访管中断、算术操作溢出等各种程序性中断。其中访管中断是由机器指令提供的特殊指令,该指令执行时将会引起内中断。异常是不能被屏蔽的,一旦出现应立即响应并加以处理。

中断和异常的区别如下:

  • 中断:中断是由与现行指令无关的中断信号触发的,所以它是异步的,而且中断的发生与 CPU 处在用户模式或内核模式无关,通常在两条机器指令之间才可以响应中断,一般来说,中断处理程序提供的服务不是为当前进程所需要的,如时钟中断、硬盘读写服务请求中断
  • 异常:异常则是由处理器正在执行现行指令而引起的。因而一条指令执行期间允许响应陷入。通常,异常处理程序提供的服务是为当前进程所用的。异常包括很多方面,有出错,也有陷入。出错陷入的主要一点区别是:它们发生时保存的返回指令地址不同,出错保存指向触发异常的那条指令,而陷入保存指向触发异常的那条指令的下一条指令。因此,当从异常返回时,出错会重新执行那条指令,而陷入就不会重新执行那条指令。

上述的内中断与外中断(中断和异常)要通过硬件设施来产生中断请求,可以看作硬中断。与其相对应的不必由硬件发信号而能引发的一种中断称为软中断,软中断是利用硬件中断的概念,用软件方式进行模拟,实现宏观上的异步执行效果。它通常是由内核或进程对某个进程发出的中断信号,可以看作内核与进程或进程与进程之间用来模拟硬中断的一种信号通信方式。

软中断和硬中断两者的共同点是:当中断源产生中断请求或发出软中断信号后,CPU 或者接收进程在适当的时机自动进行中断处理或完成软中断信号所对应的功能。这里所说适当时机表示接收的硬中断会及时获得中断处理程序的处理,但接收软中断信号的进程不一定正好在接到此信号时占有处理器,而相应的软中断信号处理必需等到该接收进程获得处理器后才能进行,通常会有一定时间的延迟。

中断装置

发现中断源并产生中断的硬件称中断装置,这些硬件包括中断逻辑线路和中断寄存器。

迄今为止,所有的计算机系统都采用硬件和软件(硬件中断装置和软件中断处理程序)结合的方法实现中断处理。

一般来说,硬件中断装置主要做以下三件事:

  • 发现中断源,响应中断请求。当发现多个中断源时,它将根据规定的优先级,先后发出中断请求。
  • 保护现场。将运行程序中断点在处理器中某些寄存器内的现场信息(又称运行程序的执行上下文)存放于内存储器。使得中断处理程序运行时,不会破坏被中断程序的有用信息,以便在中断处理结束后能够返回被中断程序继续运行。
  • 启动处理中断事件的中断处理程序,处理器状态已从目态被切换到管态。

中断来源于正在执行的程序以及计算机系统的各个部件,甚至计算机的外部环境。当一个具体的中断事件发生时,计算机的硬件中断装置必须把它记录下来。中断寄存器是用来记录中断事件的寄存器,中断寄存器的内容称中断字,中断字的每一位对应一个中断事件。每当一条机器指令执行结束的时刻,中断控制部件扫描中断字,查看是否有中断事件发生,若是则处理器便响应这个中断请求。

当中断发生后,中断字的相应位会被置位。由于同一时刻可能有多个中断事件发生,中断装置将根据中断屏蔽要求和中断优先级选取一个,然后把中断寄存器的内容送入程序状态字寄存器的中断码字段,且把中断寄存器的相应位清“0”当处理中断事件的程序执行时就可以读出中断信息进行分析,从而知道发生了什么中断事件。

紧接着中断装置进行必要的保护现场工作。此时并不一定要将处理器中所有寄存器中的信息全部存于(写回)存储器中,但是,对程序状态字寄存器中的那些信息一定要保护起来。最后,将中断处理程序的程序状态字送入现行程序状态字寄存器,这就引出了相应的中断事件处理程序。

IBM中大型机中断响应过程

如果把被中断的程序的程序状态字称为旧程序状态字,而把中断处理程序的程序状态字称为新程序状态字的话,如何来实现新旧程序状态字的交换呢?通常,系统为每一种中断都开辟了主存的固定单元存放新的和旧的程序状态字。

下图是 IBM 中大型机中断响应过程,主存中开辟了专用的双字单元(用16进制标出),用于存放各类中断的旧的和新的 PSW(分别为旧的和新的外中断、访管中断、程序中断、机器故障中断和 I/O 中断),CPU 中还有硬件程序状态字寄存器保存运行程序的现行 PSW。

IBM PC 中断的响应过程

当响应中断时,由硬件执行 1 把中断码装配到现行 PSW 中,然后,执行 2 把现行 PSW 保存到中断类相应的旧 PSW 单元;同时,执行 3 把中断类相应的新 PSW 加载到现行 PSW ,这就引出了相应中断类的中断处理程序。中断事件处理结束后,如果执行 4 便可从断点返回继续执行被中断的程序。

在 IBM PC 机上,为了方便地找到中断处理程序,通常在计算机内存的低地址处开辟了一个称为中断向量表的区域。表中每一项称为一个中断向量,其中存放了一个中断处理程序的入口地址及相关信息,不同中断源需要用不同的中断处理程序处理,也就对应了不同的中断向量。

另外,采用堆栈方式保存被中断程序的状态信息,当发现中断源并响应中断时,中断装置将把现行 PSW 内容压进堆栈,接着再把指令指针 IP 和代码段基地址内容也压进堆栈,这就保存了原运行程序的状态。处理器根据硬件中断装置提供的中断向量号,获得被接受的中断请求的中断向量地址,再按照中断向量地址把中断处理程序的 PSW 送入现行程序状态字寄存器,加载新的程序状态字。从而,就引出了处理特定中断事件的中断处理程序。返回原程序时,只要把栈顶内容弹出送入现行 IP、CS和PSW中。

中断处理程序

处理中断事件的程序称为中断处理程序。它的主要任务是处理中断事件和恢复正常操作。由于不同中断源对应不同中断处理程序,故快速找到中断处理程序的入口地址是一个关键问题。寻找入口地址可用如下办法:在主存储器(常在低地址区)设置一张向量地址表,存储单元的地址对应向量地址,存储单元的内容为入口地址。CPU 响应中断后,根据预先规定的次序找到相应向量地址,便可获得该中断事件处理程序的入口地址。

一个操作系统设计者将根据中断的不同类型和不同的应用环境,来确定不同的处理原则。 具体地讲,一个中断处理程序主要做以下四项工作:

  1. 保护未被硬件保护的一些必需的处理状态。例如,将通用寄存器的内容保存到主存储器,从而使中断处理程序在运行中可以使用通用寄存器
  2. 识别各个中断源,分析产生中断的原因。
  3. 处理发生的中断事件。中断处理程序将根据不同的中断源,进行各种处理操作。有简单的操作,如置一个特征标志;也有相当复杂的操作,如重新启动磁带机倒带并执行重读操作。
  4. 恢复正常操作。恢复正常操作一般有几种情况:恢复中断前的程序按断点执行;重新启动一个新的程序或者甚至重新启动操作系统。

中断事件的具体处理方案

机器故障中断事件的处理

一般来说,这种事件是由硬件的故障产生的,排除这种故障必须进行人工干预。中断处理能做的工作一般是保护现场,防止故障蔓延,报告给操作员并提供故障信息以便维修和校正,以及对程序中所造成的破坏进行估价和恢复。下面列举一些硬件失效中断事件的处理办法。

电源故障的处理

当电源发生故障,例如断电时,硬设备能保证继续正常工作一段时间。操作系统利用这段时间可以做以下三项工作:

  • 将处理器中有关寄存器内的信息经主存储器送到磁盘保存起来,以便在故障排除后恢复现场,继续工作。
  • 停止外围设备工作。有些外围设备(例如磁带机)不能立即停止,中断处理程序将把这些正在交换信息又不能立即停止的设备记录下来。
  • 停止处理器工作。一般可以让主机处于停机状态,此时,整个系统既不执行指令又不响应中断。

当故障排除后,操作员可以从一个约定点启动操作系统以恢复工作。恢复程序做的主要工作是:

  • 恢复中断前的有关现场
  • 启动被停止的外围设备继续工作
  • 如果发生故障时有不能立即停止的外围设备正在工作,那么,涉及这些外围设备的程序将被停止执行而等待操作员的干预命令

完成上述各项工作后,系统将选择可以运行的程序继续运行

主存储器故障的处理

主存储器的奇偶校验或海明校验装置发现主存储器读写错误时,就产生这种中断事件。中断处理程序首先停止与出现的中断事件有关的程序的运行。然后向操作员报告出错单元的地址和错误的性质。

程序性中断事件的处理

处理程序性中断事件大体上有两种办法。

对于那些纯属程序错误而又难以克服的事件,例如非法使用特权指令,企图访问一个不允许其使用的主存储器单元等,操作系统只能将出错程序的名字、出错地点和错误性质报告给操作员并请求干预。

对于其他一些程序性中断,例如定点溢出、阶码下溢等,不同的用户往往有不同的处理要求。所以,操作系统可以将这种程序性中断事件转交给用户程序自行处理。

如果用户程序对发生的中断事件没有提出处理办法,那么操作系统将进行标准处理。

用户怎样来编制处理中断事件的程序呢?有些语言提供了称之为 on 语句的调试语句,它的形式如下:

1
on    <条件>    <中断续元入口>

比如:

1
2
3
4
5
6
7
8
9
// 每当发生定点溢出时,转向以 LA 为标号的语句
on  fixed overflow  go to LA; 

// 对于发生在不同地方的同一种程序性中断事件允许用户采用不同的处理方法。

// 例如,在执行了上述调试语句后又执行调试语句:
on  fixed overflow  go to LB;

// 就表示今后再发生溢出时将转向 LB 而不是转向 LA 去处理了

有了调试语句后,用户用程序设计语言编制程序时,也就可以编写处理程序性中断事件的程序了。编译程序为每个用户设置一张中断续元入口表,且在编译源程序产生目标程序时,把调试语句翻译成一段程序。其功能是:将中断续元入口地址送入中断续元入口表中对应该语句的中断条件的那一栏。中断续元入口表的形式如图:

中断续元入口

对应每一个用户处理的中断事件,表格中有一栏用以填写处理该中断事件的中断续元入口地址。如果用户没有给出处理其中断事件的中断续元时,相应栏的内容为0。当程序运行执行到调试语句时,就将中断续元的入口地址送入相应栏内。

显然,对于同一中断事件,当执行第二次对应该事件的调试语句时,就将第二次规定的中断续元入口地址填入表内相应栏中而冲去了第一次填写的内容。这就是上面所说的,利用对同一条件多次使用调试语句时,可以做到对发生于不同地点的同一种中断事件采用不同的处理方法。

当发生程序中断事件后,操作系统是怎样转交给用户程序去处理的呢?操作系统只要根据中断事件查看表中对应栏,如果对应栏为“0”它表示用户未定义该类中断续元,此时系统将按规定的标准办法进行处理。例如,将程序停止下来,向操作员报告出错位置和性质,或者置之不顾,就好像什么事也没有发生一样。如果对应栏不为“0”,则强迫用户程序转向中断续元去处理。

如果在中断续元的执行中又发生中断事件时,就不能这样简单地处理了。首先,中断续元的嵌套一般应规定重数,在上面的表格中规定嵌套重数为2。表格第一栏的第 0 字节记录了第一次进入中断续元的事件号;第 1 个字节记录了第二次(嵌套)进入中断续元的事件号。其次,中断续元的嵌套不能递归,例如,处理定点溢出的中断续元,在执行时不允许又发生定点溢出程序性中断事件。

下面按步骤小结一下中断续元的处理过程和原则:

  • 编译程序编译到 on 语句时,生成填写相应中断续元入口表的目标代码段;
  • 程序运行执行到 on 语句时,根据中断条件号,将中断续元入口填入相应栏,这是通过执行上述代码段来实现的;
  • 执行同一中断条件号的 on 语句时,中断续元入口被填入同一栏,从而,用户可在他的程序的不同部分对同一中断条件采用不同的处理方法;
  • 每当一个中断条件发生时,检查中断续元入口表相应栏,或转入中断续元处理,或进行操作系统标准处理;
  • 程序性中断处理允许嵌套,应预先规定嵌套重数,但不允许递归。
外部中断事件的处理

时钟定时中断以及来自控制台的信息都属外部中断事件,它们的处理原则如下:

时钟中断事件的处理

时钟是操作系统进行调度工作的重要工具,如让分时进程作时间片轮转、让实时进程定时发出或接收控制信号、系统定时唤醒或阻塞一个进程、对用户进程进行记账。

时钟可以分成绝对时钟和间隔时钟(即闹钟)两种。利用计时器能确保操作系统必要时获得控制权,例如,陷入死循环的进程最终因时间片耗尽会被迫出让处理器。

系统设置一个绝对时钟寄存器,计算机的绝对时钟定时地(例如每 10 ms)把该寄存器的内容加 1。如果开始时这个寄存器的内容为 0,那么,只要操作员告诉系统开机时的年、月、日、时、分、秒,以后就可推算出当前的年、月、日、时、分、秒了。当绝对时钟寄存器记满溢出时,就产生一次绝对时钟中断,操作系统处理这个中断时,只要在主存的固定单元上加 1 就行了。这个固定单元记录了绝对时钟中断的次数,这样就可保证有足够的计时量。计算当前时间时,只要按绝对时钟中断的次数和绝对时钟寄存器的内容推算就可得到。

间隔时钟是定时将一个间隔时钟寄存器的内容减 1,当间隔时钟寄存器的内容为 0 时,就产生一个间隔时钟中断。所以,只要在间隔时钟寄存器中放一个预定的值,那么就可起到闹钟的作用,每当产生一个间隔时钟中断,就意味着预定的时间到了。操作系统经常利用间隔时钟作控制调度。

时钟硬件做的工作仅仅是按已知时间间隔产生中断,其余与时间有关的任务必须由软件来做,不同的操作系统有关时钟的任务不同,但一般包括以下内容:

  • 维护绝对日期和时间
  • 防止进程的运行时间超出其允许值,发现陷入死循环的进程
  • 对使用 CPU 的用户进程记账
  • 处理进程的间隔时钟(闹钟)
  • 对系统的功能或部件提供监视定时器

在 Intel x86/奔腾 微机中,Linux利用 CMOS 中记录的时间作为系统启动时的基准时间,在系统运行时,利用时钟滴答来维护系统的时间。Linux 使用一个全局变量称 jiffies(瞬时)作为所有系统时间的测量基准,系统启动时,CMOS 中记录的时间转化为从 1970年1月1日0时0分0秒(UNIX纪元)算起的 jiffies 值(累积秒数)。操作系统中需要有定时服务的机制,以实现准时调度任务或处理与时间相关的工作,这些都是通过定时器机制来实现的。

Linux定时器机制

Linux 中存在两种类型的系统定时器,这两种定时器都具有对应的处理例程,必须在到达给定的系统时间时被进程调用,但实现方法有些不同。第一类是老的定时器机制,有一个 CA 个指针的数组定义的定时器。每个指针可指向一个 timer-struct 结构,而 timer-active 是活动定时器的掩码,数组元素是静态定义的,在系统初始化时入口被加到该数组中。第二类是新的定时器机制,突破了 32 个定时器的限制,使用一个 timer-list 数据结构的链表,按定时器到期时间的升序排列。两类定时器中 expires 给出该定时器被激活的时间,而 *fn() 指出定时器激活后的处理函数。

两类定时器都使用 jiffies 值作为到期比较时间。例如,某个定时器要在 2s 之后到期,则必须将 2s 转换成对应的 jiffies 值,加上当前的系统时间(也是以 jiffies 为单位)后,得到的便是该定时器到期的系统时间 expires。每次系统时钟滴答到来时,定时器 bottom half 处理过程被标记为活动状态,这样当调度程序下次运行时,定时器队列能获得处理。定时器 buttom half 处理过程要处理两种类型的系统定时器。对于老的系统定时器,检查 timer-active 中被置位的位掩码,以便确定活动的定时器。如果一个活动的定时器到期,便调用对应的定时器例程,timer-active 对应位被清除。对于新的系统定时器,检查链表中的 timer-list 数据结构。每个到期的定时器将被从链表中移出,对应的定时器例程被调用。新的定时器机制的优点是能传递参数 data 到定时器例程中。

有了上述定时器,Linux 就可以统计用户的记账信息,它记录进程的创建时间及进程在生命周期占用的 CPU 时间。每个时钟滴答到来时,核心都修改当前进程在内核态和用户态占用的时间,这些时间称为记账信息。对于不同的时间,Linux 运行了不同的间隔定时器,这些间隔定时器的类型有三种:

  • real 这种间隔定时器按实际时间计时,不管进程处在何种模式下运行(包括进程被挂起时),计时总在进行,当定时到达时发送给进程一个 SIGALRM 信号
  • virtual 这种间隔定时器仅当进程在用户态下执行时才计时,当定时到达时发送给进程一个 SIGVTALRM 信号
  • profile 这种间隔定时器是当进程执行在用户态或核心态时都计时,当定时到达时发送给进程一个 SIGROF 信号

Linux 允许进程同时启动多个定时器,通过在一个进程中设定上述三个定时器,就可以了解一个进程在用户态、内核态和总的执行时间。把定时器工作所需的时间值及有关信息保存在进程的 task-struce 中,可以使用系统调用设置、启动、停止定时器或读出定时器的当前值。Virtual 和 profile 定时器的工作原理相同,当前进程的定时值随时钟 tick 而递减,直到为0时,表示定时时刻到达,定时器就会发出相应定时信号。

Real 定时器的工作原理稍有不同,当使用这种定时器时,进程使用系统的 timer-list 数据结构。每次时钟滴答时它的定时值也会递减,但是当定时时刻到达时,是由时钟 bottom half 处理过程把它从定时器队列中删除,并调用间隔定时器处理程序,这一处理过程中会产生一个 SIGALRM 信号

最后,给出 Linux 时钟系统该调用。在 Linux 中,通过时钟系统调用完成系统时钟的读取、设置和校准功能。这些系统调用为计时服务提供支持,也为用户查询当前系统时间提供了接口。

  • sys-time: 读取系统时间,精度秒级。
  • sys-stime: 设置系统时间,精度秒级。
  • sys-gettimeofday: 读取系统时间和时区,精度微秒级。
  • sys-settimeofday: 设置系统时间和时区,精度微秒级。
  • sys-adjtimex: 用于在网络环境下,当本地的系统时间和网络服务器时间不符时调整系统时钟
控制台中断事件的处理

操作员可以利用控制台开关请求操作系统工作,当使用控制台开关后,就产生一个控制台中断事件通知操作系统。操作系统处理这种中断就如同接受一条操作命令一样,转向处理操作命令的程序执行。

I/O中断的处理

输入输出中断种类比较多,处理方法各异

I/O 操作正常结束后的处理

首先,把正在等待输入输出操作完成的进程设置为可执行的状态,然后要查看是否有等待该设备或通道的其他进程,若有则释放。

I/O 操作发生故障后的处理

对于设备本身的故障,可以先向相应设备发命令索取状态字节。然后进行分析就可以知道故障的确切原因。如果该外围设备的控制器有复执功能,就组织复执。如果该外围设备的控制器没有复执功能,那么,对于某些故障系统可组织软复执。对于不能复执的故障或复执多次仍不能克服的故障,系统将向操作员报告,请求人工干预。

对于启动命令的错误,例如,启动外围设备的命令要求从输入机上读入 1000 个字符,然而读了 500 个字符就遇到“停码”,输入机停止。操作系统在处理这类错误时,可以把其转交给用户,转向用户程序的中断续元,由用户自己处理。

I/O操作发生异常后的处理

如果设备在操作中发生了某些特殊事件,那么在设备操作结束发生中断时,也要将这个情况向系统报告。操作系统从设备状态字节中的设备特殊位为 1,可以判知设备在操作中发生了某个特殊事件。对于磁带机,这意味着在写入一块信息遇到了带末点或读出信息时遇到了带标。在写操作的情况,系统知道磁带即将用完,如果文件还未写完,应立即组织并写入卷尾标,然后通知操作员换卷以便将文件的剩余部分写在后继卷上。在读操作的情况,系统判知这个文件已经读完或这个文件在此卷上的部分已经读完,进行文件结束的处理;若只读了一部分,则带标后面是卷尾标,系统将通知操作员换卷,以便继续读入文件。对于行式打印机,这意味着纸用完,因此系统可暂停输出,通知操作员装纸,然后继续输出。

设备报到或设备结束的处理

如果是外围设备上来的“设备报到”或“设备结束”等异步信号,表示有外围设备接入可供使用或断开暂停使用,操作系统应修改系统表格中相应设备的状态。

自愿中断事件的处理

这类中断是由于系统程序或用户程序执行访管指令(例如, UNIX 中的 trap 指令,MS-DOS 中的 int 指令)而引起的,表示运行程序对操作系统功能的调用,所以也称系统调用,可以看出是机器指令的一种扩充。

硬件在执行访管指令时,把访管参数作为中断字并入程序状态字,同时将它送入主存指定单元,然后转向操作系统处理。操作系统的访管中断处理程序分析访管参数,进行合法性检查后,按照访管参数的要求进行相应的处理。

操作系统的基本服务是通过系统调用来请求的,是操作系统为用户程序调用其功能提供的接口和手段。系统调用机制本质上通过特殊硬指令和中断系统来实现。不同机器系统调用命令的格式和功能号的解释不尽相同,但任何机器上的系统调用都有共性处理流程。这一共性处理流程如下:

  • 用户程序执行 n 号系统调用
  • 通过中断系统进入访管中断处理,保护现场,按功能号跳转
  • 通过系统调用入口表找到相应功能入口地址
  • 执行相应例行程序,结束后正常情况返回系统调用的下一条指令执行

中断的优先级和多重中断

中断的优先级

在计算机执行的每一瞬间,可能有几个中断事件同时发生。这时,中断装置如何来响应这些同时发生的中断呢?一般说,中断装置按照预定的顺序来响应。这个按中断请求的轻重缓急的程度预定的顺序称为中断的优先级,中断装置首先响应优先级高的中断事件。

在一个计算机系统中,各中断源的优先顺序是根据某个中断源或中断级若得不到及时响应,造成计算机出错的严重性程度来定的。

当某一时刻有多个中断源或中断级提出中断请求时,中断系统如何按预先规定的优先顺序响应呢?可以使用硬件和软件两种办法。前者根据排定的优先次序做一个硬件链式排队器,当有高一级的中断事件产生时,应该封住比它优先级低的所有中断源;后者编写一个查询程序,依据优先级次序自高到低进行查询,一旦发现有一个中断请求,便转入该中断事件处理程序入口。

一种可能的中断优先级由高到低的顺序是:机器校验中断、自愿性中断、程序性中断、外部中断、输入输出中断、重启动中断。

注意:中断的优先级只是表示中断装置响应中断的次序,而并不表示处理它的先后顺序。

现代大部分计算机配置可编程中断控制器,CPU 可执行指令设置可编程中断控制器的屏蔽码,当发现屏蔽码置位的中断位有了中断也不向 CPU 申请中断。这时硬件自动保存这次中断,以便屏蔽解除再行申请中断并进行处理。

中断的屏蔽

主机可以允许或禁止某类中断的响应,如主机可以允许或禁止所有的输入输出中断、外部中断、机器校验中断以及某些程序性中断。

对于被禁止的中断,有些以后可继续响应,有些将被丢弃。有些中断是不能被禁止的,例如计算机中的电源断电中断、自愿性访管中断就不能被禁止。

主机是否允许某类中断,由当前程序状态字中的某些中断屏蔽位来决定。一般,当屏蔽位为 1 时,主机允许相应的中断,当屏蔽位为 0 时,相应中断被禁止。按照屏蔽位的标志,可能禁止某一类内的全部中断,也可能有选择地禁止某一类内的部分中断。有了中断屏蔽功能,就增加了中断排队的灵活性,采用程序的方法在某段时间中屏蔽一些中断请求,以改变中断响应的顺序。

多重中断事件的处理

在一个计算机系统运行过程中,由于中断可能同时出现,或者虽不同时出现但却被硬件同时发现,或者出现在其他中断正在进行处理期间,这时 CPU 又响应了这个新的中断事件,于是暂时停止正在运行的中断处理程序,转去执行新的中断处理程序,这就叫多重中断(又称中断嵌套)。

一般来说,优先级别高的中断有打断优先级别低的中断处理程序的权利,但反之则不允许优先级别低的中断干扰优先级别高的中断处理程序的运行。

对于多个中断,可能是同一中断类型的不同中断源,也可能是不同类型的中断。对于前者,一般由同一个中断处理程序按预定的次序分别处理之;对于后者,可以区别不同情况做如下处理:

  • 禁止再发生中断。在运行—个中断处理程序时,对任何新产生的中断不予理睬,这可以通过屏蔽某些中断来实现。这种方法简单易行,所有中断都严格按顺序处理,但没有考虑相对优先级和时间限制的要求。
  • 定义中断优先级。对于有些必须处理且优先级更高的中断源,采用屏蔽方法有时可能是不妥的,因此在中断系统中往往允许在运行某些中断例行程序时,仍然可以响应中断。这时,系统应负责保护被中断的中断处理例行程序的现场(有的计算机中断系统对断点的保存是在中断周期内,由中断隐指令实现,对用户是透明的),然后再转向处理新中断的例行程序,以便处理结束时可以返回原来的中断处理例行程序继续运行。操作系统必须预先做出规定,哪些中断类型允许嵌套?嵌套的最大级数?嵌套的级数视系统规模而定,一般不超过三重为宜,因为过多重的“嵌套”将会增加不必要的系统开销。
  • 响应并进行中断处理。在运行中断处理例行程序时,如果出现任何程序性中断源,一般情况下,表明这时中断处理程序有错误,应立即响应并进行处理。

每个中断处理程序的程序状态字中,究竟应该屏蔽哪些中断源,将由系统设计而定,需要考虑的情况有:硬件的中断优先级,应用的需要,软件处理所希望的优先级,可能丢失的中断源及其对系统的影响等。

Linux 中断处理

Linux 中断处理过程

Linux 中断处理子系统的任务之一是当中断发生时调用正确的中断处理例程。为了实现这一点,使用了两个数据结构:irq-action 和 irqaction,其中,irqaction 中含有处理一种中断所需的各种信息:

Linux struct irqaction

其中,包括函数指针 handler, 指向中断例程地址;特征位 flags,如是否允许中断嵌套;SPARC64 平台需使用的 mask;生成中断的硬件设备名字 name;硬件厂商定义的硬件类型标识符 dev-id 和共享 IRQ 时,队列中下一个 irqaction 结构的指针 next 等。irq-action是一个向量,其中的每一个元素是一个指向 irqaction 的指针。

外设组件互连 PCI(Peripheral Component Interconnect) 是 PC 机上的一个外设组件互连标准,在高端服务器上,为了使用更多 PCI 设备,需要采用 PCI 桥技术。这样一来,PCI 中断源的数目可能超过中断控制器的中断引脚数,可以让多个 PCI 设备共用一个中断引脚,这就是设备的中断共享。在 Linux 中,实现中断共享时让某个中断号对应多个 PCI 设备,由一个中断源的第一次中断请求声明是否允许共享中断。使用一个 irq-action 指向多个链接起来的 irqaction,而且链表上的中断处理例程应为相同类型

当中断发生时,Linux 首先根据中断控制器的状态寄存器确定中断源,再根据中断源的编号计算出该中断在 irq-action 中的偏移量。如果该中断源设备相应的 irqaction 不存在,系统会在日志中记录一个错误,否则根据 irqaction 中的地址调用中断处理例程。当共享中断发生时,系统调用该中断所对应的所有中断处理例程(irqaction中的handler),即使某个设备未发生中断,其中断处理例程也会被调用。

当设备驱动程序中的中断处理子例程被核心调用时,中断处理例程必须找出发生中断的原因并作出处理。为了找出发生中断的原因,设备驱动程序必须读出中断设备的状态寄存器,发生中断的原因可能是设备完成操作或产生了某种错误。一旦确定了中断发生的原因,设备驱动程序就可进行相应的处理。

Linux 核心含有多种设备驱动程序,并且有如下一些特性:

  1. 核心代码 设备驱动程序是系统核心的重要组成部分,代码都是核心代码。如果出现错误,会造成系统严重破坏,如破坏文件系统或丢失数据。
  2. 核心接口 设备驱动程序提供标准的核心接口,如终端设备驱动程序提供文件 I/O 接口,SCSI 设备驱动程序提供文件 I/O 接口及缓存区缓存接口等。
  3. 核心机制和服务 设备驱动程序使用标准的系统服务,如内存分配、等待队列等来完成操作,还可以利用核心系统服务:请求中断、允许中断、屏蔽中断或向系统注册设备专用的中断处理例程。此外,设备驱动程序还具有可装载性:它可以核心模组形式动态地装入到核心和从中卸出;可配置性:在编译核心时指定并配置到核心等特性。
快中断与慢中断

在 Linux 中,,可以区分快中断和慢中断两类中断事件。前者用于时间短、简单的中断处理任务;而后者处理常见的中断,需要时间较长且处理复杂。

两者的主要区别为:

  1. 处理慢中断前需保存所有寄存器的内容,而快中断处理最初保存现场时,仅要保存那些被常规 C 函数修改的寄存器。
  2. 在慢中断处理时,通常不屏蔽其他中断信号,而快中断处理时会屏蔽所有其他中断。
  3. 慢中断处理完毕后,通常不立即返回被中断的进程,而是进入调度程序重新进行调度,调度结果未必是被中断的进程运行(是抢先式调度)。而快中断处理完毕后,通常恢复现场返回被中断的进程继续执行(是非抢先式调度)

为了尽快缩短快中断处理时间,以便及时响应处理期间到达的其他中断信号,便引入了底半处理的概念。

底半处理(bottom half handing)

发生中断时,处理器暂停当前执行的指令,系统负责把中断发送到相应的设备驱动程序去处理。

由于设备驱动程序都是核心态代码,通常在中断处理过程中,系统要关闭中断,不再能进行其他任何工作,这段时间里系统资源的利用率是非常低的。为了缩短屏蔽中断的时间,希望设备驱动程序以最快的速度在核心态下完成与中断事件有关的处理,而把中断事件的其他大部分耗时的工作留在中断处理例程之外,由系统自行安排运行时机(不在中断服务上下文中)执行。这种一部分工作由核心代码在关中断状态下处理,另外一部分工作由非核心态代码来处理的方式称为底半处理(bottom half handing)。

Linux 用底半处理机制来实现中断事件的快速处理,它可以让设备驱动程序和核心其他部分将这些工作进行排序以延迟执行,可见 底半处理 是一种任务延迟处理机制。于是,Linux 的中断服务例程可以分成top halfbottom half两部分。其中前者就是在中断向量表中登记的中断服务例程的入口部分,每当中断事件发生时,在关中断状态下 top half 处理过程立即执行,但是 bottom half 处理过程却被推迟并可打开中断执行,这是通过把 top half 处理过程和 bottom half 处理过程分立为独立的函数并对其区别对待实现的。top half 处理过程要决定其相关的 bottom half 处理过程是否需要执行,不能推迟的部分显然不会属于 bottom half。但可以推迟的部分总让它属于底半处理部分,再去排队等待工作,这一任务是通过设置 bh-active 中的一个位来标志的。

因此,当处理快中断时,如果有其他中断到达———不论是快中断还是慢中断,它们都必须等待。为了尽可能快地处理来到的其他中断,内核就需要尽可能地将耗时的处理延迟到底半处理过程执行。

使用 底半处理 的另一个原因是,中断服务例程的底半处理过程包含有一些中断所不一定非要执行的操作,只要内核可以在一系列中断之后从某些地方得到。在这种情况下,执行中断的底半处理过程是一种浪费,它可以稍稍延迟并在以后仅执行一次。

例如,top half 处理过程已标记 bottom half 处理过程必须执行,这种标记可能重复多次。如果在内核有机会运行 bottom half 处理过程之前给定的设备就已经发生了 100 次中断,那么,中断服务程序的 top half 处理过程就运行 100,底半处理过程只要运行 1 次。bottom half 处理在 Linux 内核中又被认为是“软中断处理”。很多情况下,软中断又是和硬中断相对应的,“硬中断”是外部设备对 CPU 的中断,top half 是硬中断;同时,“软中断”通常是硬中断服务程序对内核的中断,bottom half 是软中断;而“信号”也是一种软中断,“信号”是由内核或进程对其他进程的中断。

任务队列

在 Linux 内核中设立了任务队列,这是核心对任务进行延迟处理的一种方法,提供了对任务队列中任务排队及处理的通用机制。遇到一个任务时,可以立即被处理,也可以把它插入某个队列留在稍后再行处理。

简单地说,底半处理要利用到队列机制。底半处理是中断的下半部分,在下半部分的处理中可以挂接多个任务(其实是挂接函数,比如一些处理例程handler()),这时就要用到任务队列。队列在 Linux 中应用相当广泛,不止在底半处理中要用到,还在其他许多场合被使用。但是任务队列可以由用户动态定义和管理,而底半处理过程却是由 Linux 静态定义的,且不能超过 32 种。

任务队列由 tq-struct 单向链表结构组成,如图所示。每个 tq-struct 数据结构是任务队列的节点,它包含一个处理例程地址、指向一些数据的指针和下一个任务节点指针。当任务队列中的节点被处理时,将调用例程并传递参数指针。

Linux 任务队列

Linux 核心中的任何部分,如设备驱动程序,都可以建立并使用队列机制,核心建立和维护三个一般性任务队列:

  1. 定时器队列(TQ-TIMER) 该队列用来对在系统时钟信号到来之后需要尽快处理的任务进行排队。每次时钟滴答到来时,都要检查定时器队列是否为空,如果不空则要把定时器的底半处理过程标记为活动状态,以便激活此任务。在进程调度下一次执行时,定时器的底半处理过程被调用,定时器队列中排队的任务也就被处理了。
  2. 即时队列(TQ-IMMEDIATE) 调度程序对的底半处理过程进行处理时,即时队列中的任务立即得到处理。即时底半处理过程的优先级较低,因此,与定时器的底半处理过程相比,这个队列中的任务处理要稍迟一些。
  3. 进程调度队列(TQ-SCHEDULER) 该任务队列由进程调度程序直接处理,主要用来支持系统中的其他任务队列。
底半处理数据结构

下图描述了内核的底半处理的数据结构,最多能有 32 个不同的底半处理过程。 bh-base 是底半处理过程的入口指针,bh-mask 和 bh-active 共同控制的底半处理过程能否运行,分别指明是否有底半处理过程被安装和是否有效的位设置,若 bh-mask 的位 n 被置位则 bh-base 的第 n 个元素包含一个底半处理过程的入口地址。若 bh-active 的位 n 被置位,则第 n 个处理程序能被调度程序任意调用,只要对 bh-maskbh-active 进行位“AND”运算就能够表明应该运行哪一个底半处理过程,如果“位与”运算的结果为 0,就没有底半处理过程需要运行。这些索引在入口表中被静态设置,计时器的底半处理过程入口在索引 0,具有最高优先级;控制台的底半处理过程入口在索引 1 等等。典型的底半处理过程有一个任务队列相关联,例如,用于多个设备驱动器排队工作的通用处理程序是 immediate,它的底半处理过程通过包含需要被立即实现的任务的任务队列(tq-immediate)工作,当调度程序调用该底半处理过程时,它就会处理与之相关的任务队列中的各个任务。

底半处理数据结构
底半处理的执行过程

核心中的某些底半处理过程是和特定设备相关的,而其他底半处理过程则可以灵活地定义。

多数低半处理过程与设备驱动有关,由核心定义的部分通用底半处理过程有:

  1. 定时器(TIMER-BH) 当系统周期性定时信号(时钟中断)到来时,定时器底半处理过程被标记为活动状态,并被系统用来驱动核心的定时器队列机制。
  2. 控制台(CONSOLE-BH) 处理进程控制台消息的底半处理过程。
  3. 消息队列(TQUEUE-BH) 处理进程消息的底半处理过程。
  4. 串行口(SERIAL-BH) 串行口底半处理过程。
  5. 网络(NET-BH) 用于一般网络处理的底半处理过程。
  6. SCSI设备(SCSI-BH) 用于 SCSI 设备底半处理过程。
  7. 即时(IMMEDIATE-BH) 一种一般性处理过程,许多设备驱动程序利用该过程对要在随后处理的任务进行排队的通用底半处理过程。
  8. 键盘(KEYBOARD-BH) 键盘消息的底半处理过程。

只要一个设备驱动器,或核心的其他一些部分需要对某些任务进行排队处理时,系统把任务添加到适当的任务队列(如核心的定时器队列)中,然后通知核心进行底半处理。

具体做法是需要设置bh-active的适当位来完成。例如,如果设备驱动器在 immediate 队列上将某个任务排队,并希望运行 immediate 的底半处理过程来处理排队的任务。由于 bh-active 的位掩码的相应位已置为 1,在下面三种情况下:

  • 当调度程序欲选择下一个运行进程之前(schedule())
  • 当从系统调用返回之前(ret_from_syscall())
  • 当每个中断处理和异常处理返回前(ret_from_intr() 和 ret_from_exception()) 系统都要检查 bh-active 中的每个位,如果有任何位被置位,则会调用 do_bottom_half() 执行有效的底半处理过程。首先检查位0,然后位1,直到31位。bh-active的位在每一个底半处理过被调用时清除。有关的数据结构和函数参阅 include/linux/interrupt.hkernel/softirq.c 文件

下面通过定时器中断(零号中断 IRQ0) 的例子来说明中断服务例程的 top half 与 底半处理过程之间的联系。定时器中断服务例程(函数)叫 timer-interrupt,它的top half函数为 do-timer 而它的 bottom-half 函数叫 timer-bh。当产生定时器中断时,timer-interrupt 从 CPU 计时器中读取数据后,调用top halfdo-timer()做如下工作:更新全局变量 jiffies(加1),该值记录了机器启动以来系统时钟滴 答 的 次 数;递 增 丢 失(没 有 被 底 半 处 理 过 程 处 理、也 就 是 两 次bottom half处理之间的滴答次数)的定时器滴答数;执行 mark-bh 标记底半处理过程为活动状态;如果定时器队列有任务在等待,标记底半处理过程准备好运行。此外,还调用 update-process-times() 更新进程的时间片及修改进程的动态优先级。整个定时器中断处理看起来十分简单,这是因为有很大一部分工作都被延迟到底半中处理了。timer-bh需要调用 update-times()run-times-list() 做更新进程和内核有关时间的统计数字。涉及有关的时间的统计数据有:系统当前的平均负载、当前时间的全局变量、当前运行进程在核心态和用户态使用的以 jiffies 为单位的时间值等。此外,它的一个主要任务是执行定时器的操作,检查和执行定时服务。

Linux软中断机制

在 Linux 内核中,bottom half 最初用于在特权级较低的上下文中完成中断服务的非关键耗时动作,现在也用于一切可在低优先级的上下文中执行的异步动作。最早的 bottom half 实现是借用了中断向量表,在 v2.4.x 的内核中仍然可以看到

1
static  void  (*bh_base[32])  (void);

系统定义了一个函数指针数组,共有 32 个函数指针,采用数组索引来访问,与此相对应的是一套函数:void init_bh (int nr, void(*routine(void))); void remove_bh(int nr) 和 void mark_bh(int nr) 等配合工作。但在 Linux 新版中 bh 语义和使用方式已经很不一样了,这三个函数仅仅是在接口上保持了向下兼容,在实现上一直都在随内核的软中断机制在变化。现在,在 v2.4.x 内核里,它用的是特殊的软中断——tasklet机制。

介绍 tasklet 之前,有必要先看一下 task queue 机制。显而易见,原始的 bottom half 机制有很大的局限性,最重要的就是软中断个数限制在 32 个以内,随着系统硬件的越来越多,软中断的应用范围越来越大,这个数目显然是不够用的,而且,每个 bottom half 上只能挂接一个函数,也是不够用的。因此,在 v2.0.x 内核里,已经用任务队列 task queue 的数据结构,这里使用的是 v2.4.2 中的实现:

Linux task queue 结构

run_task_queue(task_queue* list) 函数可用于启动list中挂接的所有 task,可以挂接在 bottom half 向量表中来启动,从而可以用于扩充 bottom half 的个数。此时,只需要调用 mark_bh(IMMEDIATE_BH),让 bottom half 机制在合适的时候调度它。

可见,task queue 以 bottom half 为基础;而 bottom half 在 v2.4.x 中则以新引入的 tasklet 为实现基础。之所以引入 tasklet,是因为 bottom half 是串行处理的,不能适应 SMP 环境,提高 SMP 多个 CPU 的利用率:不同的 tasklet 可以同时运行于不同的 CPU 上,相同的 tasklet 不会同时在不同的 CPU 上运行。

tasklet 结构与 tq_struct 比较,可以看出,tasklet 扩充了一点功能,主要是 state 属性,用于 CPU 间的同步。tasklet 的使用比 task queue 更简单,而且 tasklet还能更好的支持 SMP 结构,因此,在新的 v2.4.x 内核中,tasklet是建议的异步任务执行机制。

从前面的讨论可以看出,task queue 基于 bottom halfbottom half 基于 tasklet,而tasklet则基于softirq。可以这么说,softirq沿用的是最早的bottom half 思想,但在这个bottom half机制之上,已经实现了一个更加庞大和复杂的软中断子系统。和 bottom half 类似,系统也预定义了 4 个softirq_vec[] 结构,通过如下枚举表示:

1
2
3
4
5
6
7
enum
{
    HI_SOFTIRQ=0,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    TASKLET_SOFTIRQ
};

HI_SOFTIRQ 被用于实现 bottom half, TASKLET_SOFTIRQ 用于公共的 tasklet 使用,NET_TX_SOFTIRQNET_RX_SOFTIRQ 用于网络子系统的报文收发。在软中断子系统初始化softirq_init() 时,调用了open_softirq()HI_SOFTIRQTASKLET_SOFTIRQ 做了初始化。do_softirq()用于处理软中断,当发现那个软中断标志置位,就调用相应的软中断处理函数。do_softirq()有4个执行时机,分别是:从系统调用中返回ret_from_sys_call、从异常中返回ret_from_exception、调度程序中schedule,以及处理完硬件中断之后do_IRQ。它将遍历所有的softirq_vec,依次启动其中的 action()。需要注意的是,软中断服务程序,不允许在硬中断服务程序中执行,也不允许在软中断服务程序中嵌套执行,但允许多个软中断服务程序同时在多个 CPU 上并发执行。