1.中断的概念
中断对于操作系统非常重要,它就好像机器中的齿轮,驱动各部件的动作。所以,许多人称操作系统是由“中断驱动”的。
所谓中断是指CPU对系统发生的某个事件做出的一种反应,它使CPU暂停正在执行的程序,保留现场后自动执行相应的处理程序,处理该事件后,如被中断进程的优先级最高,则返回断点继续执行被“打断”的程序。图3-8表示中断时CPU控制转移的轨迹。
图3-8 中断示意图
引起中断的事件或发出中断请求的来源称为中断源。中断源向CPU提出的处理请求称为中断请求。发生中断时,被打断程序的暂停点称为断点。
前面讲过,中断最初是作为通道(或设备)与CPU之间进行通信的工具。通道和CPU并行工作,各自负责自己的任务。当通道完成某项预定的I/O请求或数据传输过程中发生故障时,就用中断方式向CPU“报告情况”,请求处理。
中断的概念后来得到进一步扩展。在现代计算机系统中,不仅通道或设备控制器可向CPU发送中断信号,其他部件也可以造成中断。例如,程序在CPU上运行时出现运算溢出、取数时奇偶错、电源故障、时钟计数到时等,都可成为中断源。
中断概念的另一个发展是访管(即访问管理)指令(或系统调用)的使用。用户程序中可以使用操作系统对外界提供的系统调用,得到系统内部服务。当用户程序执行到系统调用时,进程状态从用户态变为核心态。核心根据系统调用的编号,转去执行相应的处理程序,如对文件的读/写、对进程的控制等。硬件保证用户态下运行的程序不得访问核心空间中的数据,从而保护了操作系统。系统调用的出现为用户编制程序提供了方便和可靠性保证。
2.中断类型
按照不同的分类方法有不同的中断类型。
· 按功能划分
这种分类法类似于IBM 360/370系统的分类法,所有中断源分为5类。
(1)机器故障中断。它是机器发生错误时产生的中断,用来反映硬件在执行过程中出现的故障,以便进入诊断程序,做出处理。例如,机器电路检验错、电源故障和内存读数错,等等。
(2)I/O中断。这是来自通道或各种外部设备的中断,用于反映通道或设备的工作情况。例如,打印机打印结束、磁盘传输完成、利用终端进行输入/输出等。
(3)外部中断。它是来自计算机系统外部装置的中断,用来反映外界对本系统的要求。例如,计时器到时溢出,操作员操纵控制台按钮,在多机系统中它机送来信号,等等。
(4)程序性中断。这是因错误地使用指令或数据而引起的中断,用于反映程序执行过程中发现的例外情况,例如,非法操作码,无效地址和运算溢出,等等。
(5)访管中断。由于执行“访问管理程序”指令而产生的中断,用来使CPU的状态从用户态转入管理态,由操作系统根据不同的编号引进不同的处理。这样,操作系统为用户态程序提供对系统资源使用请求的服务。
· 按产生中断的方式划分
(1)强迫中断。在程序运行过程中,发生某些(个)随机性事件,如外设工作结束和程序运行出错等,需要及时进行处理的一种中断。程序设计人员在编制程序时并不知道它何时出现,也并不期望它出现。上述按功能划分的机器故障中断、I/O中断、外部中断和程序性中断都可算做强迫中断。
(2)自愿中断。程序员在编制程序时因需要系统提供某些服务而有意使用访管指令或系统调用,从而导致执行程序的中断。这是程序员事先安排好的,其出现时机是可知的。上述按功能划分的访管中断就属于这一类。
· 按中断事件来源划分
目前,很多小型机系统和微型机系统都采用这种分类方式。
(1)中断。它是由CPU以外的事件引起的,如I/O中断、时钟中断、控制台中断等。利用中断实现设备与CPU的通信。中断是异步的,因为从逻辑上讲,中断的产生与当前正在执行的进程无关。
(2)异常(Exception)。它是来自CPU内部的事件或程序执行中的事件引起的过程。如CPU本身故障(电源电压低于105 V,或频率在47~63 Hz之外)、程序故障(非法操作码、地址越界、浮点溢出等)和请求系统服务的指令(即访管指令)引起的事件等。可见,异常包括很多方面,主要有出错、陷入和可编程异常。出错和陷入之间最重要的区别是处理完异常事件返回时,出错事件会重新执行导致异常的那条指令,如缺页故障处理完之后还会尝试重新执行那条触发异常的指令(通常不会再缺页);而陷入事件则不会重新执行那条指令。陷入主要用于程序调试,被调试的进程遇到用户设置的断点会停下来等待你的处理,当你让它重新运行时,它就执行下面的指令。
可编程异常是由于用户在C程序中使用了系统调用而引发的过程。系统调用也称软件中断(或陷入)。应用程序使用系统调用就可由用户模式转入核心模式,在核心模式下完成相应的服务之后再返回用户模式。所以系统调用是用户程序与内核的接口。硬件对可编程异常的处理与对陷入的处理是一致的,即从这类异常返回时,也返回产生异常的下一条指令。
3.中断系统的作用
中断系统是当代计算机必不可少的组成部分之一。中断系统的作用主要有以下5点。
(1)提高主机的利用率,使高速CPU可以和低速的外部设备并行工作。
(2)及时进行事故处理。当计算机发生硬件故障或出现程序性错误(如运算结果溢出、除数为0、地址错、非法操作码等)时,可以通过中断系统进行处理。操作系统通过程序复执来排除偶然性错误,或将故障与错误记录下来,为故障诊断和机器恢复做好准备。
(3)实现分时操作。如前所述,在分时系统中正在运行的进程用完所分到的时间片后,就要让出CPU,排到相应的就绪队列中。在这里,依靠定时时钟对时间片进行计时,到达预定值时就产生时钟中断,调用进程调度程序进行相应处理。就是说,通过中断系统将CPU的时间分配给各个进程使用。
(4)实现实时操作。在实时控制系统中,很多信号是随机产生的,只有通过中断系统才能对它进行及时处理,避免信息的丢失。
(5)方便程序调试。利用中断可以方便地调试程序,可人为设置断点,随时中断程序的执行,查看中间结果,了解机器的工作状态,输入临时命令等。
(二)中断处理过程
1.中断的硬件结构
硬件级的中断结构与过程如图3-9所示。当I/O设备完成给定的工作之后,由相应的设备控制器产生中断信号(设操作系统已经开启中断系统),并把该信号放到总线上。当CPU检测到控制器把一个信号插到中断请求线上时,就保存少量的状态信息,如程序计数器的值,然后转到中断处理程序。
图3-9 硬件级的中断结构与过程
2.中断响应
对中断请求的整个处理过程是由硬件和软件结合起来而形成的一套中断机构实施的。发生中断时,CPU暂停执行当前的程序,而转去处理中断。这个由硬件对中断请求做出反应的过程,称为中断响应。一般说来,中断响应顺序执行下述三步动作:
(1)中止当前程序的执行;
(2)保存原程序的断点信息(主要是程序计数器PC和程序状态寄存器PS的内容);
(3)转到相应的处理程序。
通常CPU在执行完一条指令后,立即检查有无中断请求。如有,而且“中断允许”触发器为1(表示CPU可以响应中断请求),则立即做出响应。
在进行中断处理过程之前,硬件总要保存某些信息。至于保存哪些信息、保存在什么地方,这些随CPU而变。至少要把程序计数器的内容(即程序断点)自动压入堆栈,以便中断返回时,把程序计数器的值从堆栈中弹出,继续主程序的执行。
然后,形成中断处理程序的入口地址,把它送入PC寄存器中,并转入中断程序入口。通常,不同的中断有不同的入口。CPU接到中断后,就从中断控制器那里得到一个称做中断号的地址,它是检索中断向量表的位移。中断向量表的表项是中断向量。中断向量因机器而异,通常包括相应中断处理程序入口地址和中断处理时处理机状态字PSW。表3-4列出了示意性的中断向量表。例如,对于终端发出的中断,核心从硬件那里得到的中断号是2。利用它去查找中断向量表,得到终端中断处理程序ttyintr的地址。
表3-4 示意性中断向量表
采用向量中断机制的目的是为了提高检索相应处理程序的效率。表3-5是Intel Pentium处理器中断向量表的设计情况。0~31号事件是不可屏蔽的,用于对各种错误情况发出中断信号。32~255号事件是可屏蔽的,用于表示设备产生的中断。
表3-5 Intel Pentium处理器中断向量表
3.中断处理
中断响应后,就由软件(中断处理程序)进行相应处理。中断处理过程大致分为4个阶段:保存被中断程序的现场,分析中断原因,转入相应处理程序进行处理,恢复被中断程序现场(即中断返回)。中断处理的一般过程如图3-10所示。
图3-10 中断处理的一般过程
下面对软件执行的中断处理过程作进一步介绍。
(1)保存现场
保存被中断程序现场的目的是为了在中断处理完之后,可以返回到原来被中断的地方,在原有的运行环境下继续正确地执行下去。通常,中断响应时硬件已经保存了PC和PS的内容,但是还有一些状态环境信息需要保存起来。例如,被中断程序使用的各通用寄存器的内容等。因为通用寄存器是公用的,中断处理程序也使用它们。如果不作保存处理,那么即使以后能按断点地址返回到被中断程序,但由于环境被破坏(如中间运行结果丢失),原程序也无法正确运行。中断响应时硬件处理时间很短(通常是一个指令周期),所以保存现场工作可由软件来协助硬件完成,并且在进入中断处理程序时就立即去做。当然,在不同机器上两者的分工形式是不统一的。
对现场信息的保存方式是多样化的,常用方式有两种:① 集中式保存:在内存的系统区中设置一个中断现场保存栈,所有中断的现场信息都统一保存在这个栈中。进栈和退栈操作由系统严格按照后进先出原则实施。② 分散式保存:在每个进程的PCB中设置一个核心栈,一旦其程序被中断,它的中断现场信息就保存在自己的核心栈中。如在UNIX系统中每个进程都有一个核心栈。
(2)分析原因
中断处理的主要工作是根据中断源确定中断原因,然后转入相应处理程序去执行,即确定“中断源”或查证中断发生,识别中断类型(确定是时钟中断还是盘中断)和中断设备号(哪个磁盘引起的中断)。对中断源的保存有几种不同方式。在有的系统中,硬件在自动转入中断处理程序之前已把中断源信息记录在程序状态字(PSW)和相关的专用区中。如在IBM 360系统中,发生程序中断时,中断源存放在PSW的中断字段中。在I/O中断时,中断字段仅反映通道号或设备号,具体中断原因放在通道状态字(CSW)中。而在另外一些系统中,在进入中断处理程序后用指令把中断寄存器的值作为中断字取到某个寄存器或专用内存单元中保存(如NOVA机)。相应地,查找中断源一般也有两种方法:一种是顺序查询中断源状态标志;另一种是用专用指令直接获得中断源。有些计算机系统(如PDP-11机)却是由硬件根据不同的中断请求直接转入不同的中断处理程序入口的。
(3)处理中断
核心调用中断处理程序,对中断进行处理。例如,调用终端中断处理程序ttyintr,判断终端输入/输出工作是否正常完成。如果正常完成,则驱动程序便可做结束处理;如果还有数据要传送,则继续进行传送;如果是异常结束,则根据发生异常的原因做相应处理。
(4)中断返回
相应中断处理程序执行以后,要退出中断。通常要做下面两件事情:第一、选取可以立即执行的进程。通常,退出中断后,应恢复到原来被中断程序的断点,继续执行下去。如果原来被中断的进程是在核心态下工作,则不进行进程切换。如果原来被中断的进程是用户态进程,并且此时系统中存在比它的优先级更高的进程,则退出中断时要执行进程调度程序,选择最合适的进程去运行。第二、恢复工作现场。把先前保存在中断现场区中的信息取出复原。从时间顺序上讲,先恢复环境信息(各通用寄存器内容),再恢复控制信息(PS与PC)。通常,使用一条不可中断的特权指令来复原控制信息,如IBM 360的LPSW(装入PSW),PDP—11的rtt(装入PS和PC)。当然,随之也就恢复了处理机状态(用户态或管理态)。这些信息一旦“各就各位”,该进程就立即启动运行了。
(三)中断优先级和多重中断
1.中断优先级
如果在用户程序中使用系统调用,就能知道其产生中断请求的时机,除此之外,其他中断往往是随机出现的。这样,可能出现多个中断同时发生的情况。这就存在哪个中断先被响应,哪个中断先被处理的优先次序问题。为使系统及时响应并处理发生的所有中断,不发生丢失现象,在硬件设计中断机构时,就必须根据各种中断事件的轻重缓急对线路进行排队,安排中断响应次序。另外,软件在处理中断时也要相应安排优先次序。响应顺序和处理顺序可以不一样,即先响应的可以后处理。为满足某种需要,可以采用多种手段改变处理顺序,最常见的方式是采用中断屏蔽。
硬件设计时,一般把紧迫程度大致相当的中断源归并为一组,称为一个中断级。每级的中断处理程序可能有很多相似之处,可把它们统一成一个共同程序;对于不同之处,用各自的专用程序去处理。在这种方式下,每级可以只有一个中断处理程序入口,在内部处理过程中,再根据不同中断请求转入不同的子程序去分别处理。
与某种中断相关的优先权称做它的中断优先级。中断优先级高的中断在线路上有优先响应权,可以通过线路排队办法实现。在不同级别的中断同时到达的情况下,级别最高的中断源先被响应,同时封锁对其他中断的响应;它被响应之后,解除封锁,再响应次高级的中断。如此下去,级别最低的中断最后被响应。
另外,级别高的中断一般有打断级别低的中断处理程序的权利。就是说,当级别低的中断处理程序正在执行时,如果发生级别比它高的中断,则立即中止该程序的执行,转去执行高级中断处理程序。后者处理完才返回刚才被中止的断点,继续处理前面那个低级中断。但是,在处理高级中断过程中,不允许低级中断干扰它,通常也不允许后来的中断打断同级中断的处理过程。
2.中断屏蔽
(1)中断屏蔽和中断禁止
中断屏蔽是指在提出中断请求之后,CPU不予响应的状态。它常常用来在处理某个中断时防止同级中断的干扰,或在处理一段不可分割的、必须连续执行的程序时防止意外事件把它打断。
中断禁止是指在可引起中断的事件发生时系统不接收该中断信号,因而就不可能提出中断请求而导致中断。简言之,就是不让某些事件产生中断。它常用在执行某些特殊工作的条件下,如按模取余运算,算术运算中强制忽略某些中断,如定点溢出、运算溢出中断。在中断禁止的情况下,CPU正常运行,根本不理睬所发生的那些事件。
从概念上讲,中断屏蔽和中断禁止是不同的。前者表明硬件接受了中断,但暂时不能响应,要延迟一段时间,等待中断开放(撤消屏蔽),被屏蔽的中断就能被响应并得到处理。而后者,硬件不准许事件提出中断请求,从而使中断被禁止。
(2)中断屏蔽的作用
引入中断屏蔽和禁止的原因主要有以下3个方面:
电脑· 延迟或禁止对某些中断的响应。中断是可以随机发生的事件。在某些程序(如系统程序)的执行过程中,不希望外界信号对它干扰,以避免对重要数据操作的失误。此外,在某些运算(如按模同余运算)中发生一些事件(如定点溢出)是正常的,没有必要理睬。
· 协调中断响应与中断处理的关系。硬件中断排队线路只是决定若干中断同时到来时机器响应的优先次序,但处理中断的优先次序却不一定与响应的次序一致。如果中断响应时是把PSW寄存器的内容放在专用系统区中,然后取出对应处理程序的PSW装入寄存器中;接着又响应下一个级别较低的中断,那么最后被响应的中断是级别最低的,但它的中断处理程序的PSW却放在PSW寄存器中,从而使它得到优先处理。为了实现高级中断先响应也先处理,在PSW中必须设置屏蔽位,保证高级中断可以打断低级中断,而低级中断不可打断高级中断的处理。
· 防止同类中断的相互干扰。在有些系统(如IBM 360)中,同类中断只有一个中断处理程序状态字(PSW)。因此,在处理此类中断的过程中,不能响应随后到来的同类中断。否则,会因共用同一个PSW而造成混乱(后者把前者的内容冲掉)
(3)中断屏蔽的方式
屏蔽方式随机器而异,可以用于整级屏蔽,也可用于单个屏蔽。如在IBM 360/370系统中,是用PSW中某些位来屏蔽某些中断的。程序员通过特权指令设置或更改屏蔽位信息。在UNIX系统中,通常采用提高处理机执行优先级的方式屏蔽中断,即在程序状态寄存器(PS)中设置处理机当前的执行优先级,当它的值(比如6)大于或等于后来中断事件的优先级(比如4)时,该中断就被屏蔽了。
3.多重中断
多个中断可能同时出现。例如,一个程序正从通信线路上接收数据并打印结果。每当打印操作完成后,打印机会产生一个中断。每当一个数据单位到来时,通信线路控制器就会产生一个中断。数据单位可能是一个字符或是一个电脑数据块,这取决于通信规程的性质。总之,在处理打印机中断的过程中有可能出现通信中断。
处理多个中断的方法有顺序处理方式和嵌套处理方式两种。
· 顺序处理方式
当一个中断正被处理期间,屏蔽其他的中断;在该中断处理完后,开放中断,由处理器查看有无尚未处理的中断。如果有,则依次处理。这样,用户程序执行时,如果出现中断,则响应并处理它,同时屏蔽其他中断。在该中断处理程序运行完、控制返回用户程序之前,开放中断。若有另外中断未被处理,则按顺序进行处理,如图3-11(a)所示。
图3-11 多重中断的控制转移
这种方式的缺点是没有考虑中断的相对优先级或时间的紧迫程度。例如,输入数据从通信线路上到来时,就需要迅速处理,腾出空间,供后面的输入使用。如果在第2批输入到来之前,第1批输入数据还未处理完,就会丢失后面的数据。
· 嵌套处理方式
这种方式对每类中断赋予不同的优先级,允许高优先级中断打断低优先级中断的处理程序,如图3-11(b)所示。
例如,系统中有3台I/O设备:打印机、磁盘机和通信链路,各自的中断优先级分别是2、4和5。图3-12给出了一种可能的执行序列。在t=0时刻用户程序开始执行。在t=10时出现打印机中断;响应该中断,把用户信息存放在系统栈中,然后执行打印机的中断服务(处理)程序(ISR)。在t=15时,ISR还在执行,但此时发生通信中断。由于通信链路的中断优先级高于打印机中断优先级,打印机的ISR被中断,其现场信息压入栈中,接着执行通信中断的ISR。在通信ISR执行时,出现磁盘中断(t=20)。由于磁盘中断优先级低于电脑通信中断优先级,所以它只是被简单收存,而通信ISR继续执行。当t=25时,通信ISR完成,恢复先前的处理机状态,本应执行打印机的ISR,然而由于盘中断优先级高于打印机中断优先级,所以处理器执行磁盘的ISR。仅当磁盘的ISR执行完之后(t=35),打印机ISR才得以恢复执行。当打印机ISR执行完(t=40),最终把控制返还用户程序。
图3-12 多重中断示例
可以看出,嵌套中断往往会给程序设计带来困难。在有些系统(如Linux)中,当响应中断并进入中断处理程序时,CPU会自动将中断关闭。
(四)系统调用处理
1.陷入事件的处理方式
在UNIX/Linux系统中,对异常的处理称做陷入。
引起陷入的事件可以分为两组:一组是自愿进入陷入,称做自陷,如使用系统调用和断点跟踪;另一组是由于程序运行过程中出现软、硬件故障或错误,如转换无效、访问违章和非法指令等,也称做捕俘。
陷入处理的基本过程与中断处理基本相同。当执行到陷入指令(如系统调用)或出现捕俘事件时,硬件首先做出中断响应,根据取得的中断(陷入)向量进入系统核心,即该进程由用户态转到核心态。各种陷入经过断点现场保存等简单处理后,统一进入陷入处理子程序(trap)。按照参数中给出的陷入类型及陷入时处理机状态等,trap子程序对所有陷入事件按如下4种方式分别进行处理:
(1)请求系统管理人员干预。如果发生陷入时进程处于核心态,则认为出现故障。此时,系统打印若干现场信息,然后等待系统管理员干预。
(2)按用户规定方式进行处理。在用户态方式下产生的各种陷入,除系统调用和转换无效外,一般转成信号交由用户处理。
(3)用户栈自动扩充。当用户栈装满,引起转换无效故障时,系统为该进程的用户栈增加两个页面。若操作成功,该进程继续运行;否则,向该进程发信号。
(4)系统调用处理。当执行到用户程序中的系统调用时,就对陷入按系统调用方式处理。
2.系统调用的处理方式
在UNIX/Linux系统中,系统调用像C语言的普通函数调用那样出现在程序中。但是,一般的函数调用序列并不能把进程的运行模式从用户态变为核心态,而系统调用却可以做到这一点。如果说外部中断是使CPU被动、异步地进入系统空间的一种手段,那么系统调用就是CPU主动、同步地进入系统空间的手段。因为系统调用是由用户预先安排在程序的确切位置上的。
不同版本的UNIX系统提供的系统调用的数目不同,如UNIX第7版提供约50个系统调用,SVR4提供约120个系统调用。而在Linux系统中共定义了221个系统调用。这些系统调用的外在使用形式与C语言的函数调用形式相同,但实现它们的汇编代码形式通常以trap指令开头(在Linux系统中是通过中断指令“INT 0X80”实现的)。trap指令有这样一种性质:当CPU执行到trap指令时,CPU的状态就从用户态变为核心态。
trap指令的一般格式是:
trap xx
参数1
参数2
……
其中,xx表示系统调用号。如,系统调用fork的编号是2,read的编号是3,write的编号是4,等等。多数系统调用带有一个或几个参数。传递参数的方式一般有两种:通过通用寄存器(如r0、r1)的直接传送和在trap指令后自带参数。Linux内核在系统调用时通过寄存器传递参数。自带参数又分为直接和间接两种形式。直接形式是参数直接跟在trap指令之后,如上面一般形式所示;间接形式是trap指令后是一个指针,该指针指向另一条直接带参数的trap指令。
当CPU执行到trap指令时,产生陷入事件。发生中断和陷入时,硬件执行的动作基本相同,即trap指令产生陷入信号,导致CPU停止对当前用户程序的继续执行;保存当前程序计数器(PC)和处理机状态字(PSW)的值;利用相应的中断向量(所有的系统调用都对应一个中断向量)转到相应的处理程序。
所有的陷入事件有一个总的服务程序,即陷入总控程序。由于系统调用只出现在用户程序中,当时CPU必定在用户空间中运行,而陷入总控程序属于内核。所以,一旦运行陷入总控程序,CPU的运行状态就从用户态转入核心态,也就是从用户空间转入系统空间。但是,在处理系统调用的整个过程中并不自动关闭中断,即中断是开放的。
首先,陷入总控程序将有关参数压入系统栈中,以备返回用户空间、恢复现场时使用。然后,调用陷入处理程序trap。trap程序根据陷入事件的不同类型做不同的处理。对于非法指令、跟踪陷入、指令故障、算术陷入、访问违章、转换无效等事件,转入信号机构进行处理;对于系统调用事件,调用system_call(系统调用处理函数)进行处理。系统调用处理函数根据trap指令后面的系统调用号去查系统调用入口表,然后转入各个具体的系统调用处理程序。
系统调用入口表sysent的项数与系统调用号一样多。每项有3个部分:自带参数个数、标志位(如果执行setjmp函数,则置为0;否则,置为1)和相应处理程序的入口地址。表3-6列出了sysent的结构形式。
表3-6 系统调用入口表sysent结构
系统调用号就是入口表的下标。如有:
trap 4
参数1
参数2
参数3
系统根据trap后面的数字4去查sysent[4],得知这个系统调用有3个参数,且具体处理程序的入口地址是write(即write程序的起始地址)。
当该系统调用工作完成后,就回到陷入处理程序trap。它计算有关进程的优先级。如果存在比当前进程优先级更高的就绪进程,则发生进程调度,恢复另外那个进程的现场,令其投入运行,而当前进程放入就绪队列中排队。如果本进程优先级最高,则不发生重新调度。在回到本进程的用户空间之前,要判断当前进程是否收到信号。如果收到信号,则执行信号规定的动作,最后返回用户空间,执行被中断的用户程序;如果没有收到信号,则直接回到用户空间。
3.系统调用实现过程
操作系统是一个整体,各个部分的运转不是孤立的,而是相互关联、密切配合的。下面通过一个系统调用实现的全过程,说明整个操作系统是如何动态协调工作的。为了叙述简明,对系统中的进程数目、状态、资源使用等都做了简化,所以,实际系统的活动要比示例中的情况复杂得多。另外,本示例主要是介绍系统调用的一般实现过程,其中还涉及到文件管理、设备管理等内容,在后面章节中会讲到。
设用户进程A在运行中要向已打开的文件(用fd表示)写一批数据,为此在用户C源程序中可用如下系统调用语句:
rw=write (fd,buf,count);
这条语句经编译以后形成的汇编指令形式如下:
trap 4
参数1
参数2
参数3
k1:……
其中,参数1,2,3分别对应该文件的文件描述字fd,用户信息所在内存始址buf,传送字节数count。这个系统调用的执行过程见如下7步。
(1)CPU执行到trap 4指令时,产生陷入事件,硬件做出中断响应:保留进程A的PSW和PC的值,取中断向量并放入寄存器(PSW和PC)中;程序控制转向一段核心代码,将进程状态改为核心态;进一步保留现场信息(各通用寄存器的值等),然后进入统一的处理程序trap。trap程序根据系统调用号4查找系统调用入口表,得到相应处理子程序的入口地址write。
(2)转入文件系统管理。根据文件描述字fd找到该文件的控制结构—— 活动I节点,进行权限验证等操作之后,如果都合法,则调用相应的核心程序将文件的逻辑地址映射到物理块号;再申请和分配缓冲区,将进程A内存区buf中的信息传送到所分配的缓冲区中。然后,经由内部控制结构(即块设备转接表)进入设备驱动程序。
(3)启动设备驱动程序(即磁盘驱动程序),将缓冲区中的信息写到相应的盘块上。在进行磁盘I/O工作时,进程A要等待I/O完成,所以进程A让出CPU,处于睡眠状态。
(4)处理机管理做调度工作。进程调度程序从就绪队列中选中一个合适的进程,例如B,为它恢复现场,使其在CPU上运行。此时CPU在进程B的用户空间运行。
(5)当写盘工作完成后(即缓冲区中的信息都传送到盘块上),磁盘控制器发出I/O中断信号。该信号中止进程B的继续运行,硬件做出中断响应,然后转入磁盘中断处理程序。
(6)磁盘中断处理程序运行。它验证中断来源,如传输无错,则唤醒因等待盘I/O而睡眠的进程A。
(7)设进程A比进程B的优先级更高,则中断处理完成后,执行进程调度程序,选中进程A,为进程A恢复现场,然后进程A的程序接着向下执行。
上述简要过程如图3-13所示。
图3-13 系统调用实现过程示例
由上面分析可见,利用中断和陷入方式,CPU的运行状态就由用户态转到核心态。当中断、陷入处理完成后,再回用户态执行用户程序。如果说系统初启是激活操作系统的原动力,那么中断和陷入就是激活操作系统的第2动力。利用上述方式使操作系统程序得以执行,对系统的各种资源进行管理,为用户提供服务。
(五)Linux系统中shell命令执行过程
作为中断处理的另一个示例,下面介绍Linux系统中shell命令的一般执行过程。
Linux系统提供给用户的最重要的系统程序是shell命令语言解释程序。它不属于内核部分,而是在核心之外以用户态方式运行。其基本功能是解释并执行用户输入的各种命令,实现用户与Linux核心的接口。系统初启后,核心为每个终端用户建立一个进程去执行shell解释程序。它的执行过程基本上按照如下步骤进行:
(1)读取用户由键盘输入的命令行。
(2)分析命令,以命令名作为文件名,其他参数改造为系统调用execve( )内部处理所要求的形式。
(3)终端进程调用fork( )建立一个子进程。
(4)终端进程本身用系统调用wait4( )来等待子进程完成(如果是后台命令,则不等待)。当子进程运行时调用execve( ),子进程根据文件名(即命令名)到目录中查找有关文件(这是命令解释程序构成的文件),调入内存,执行这个程序(即执行这条命令)。
(5)如果命令末尾有&号(后台命令符号),则终端进程不用执行系统调用wait4( ),而是立即发提示符,让用户输入下一个命令,转步骤(1)。如果命令末尾没有&号,则终端进程要一直等待,当子进程(即运行命令的进程)完成工作后要终止,向父进程(终端进程)报告,此时终端进程醒来,在做必要的判别等工作后,终端进程发提示符,让用户输入新的命令,重复上述处理过程。
shell命令基本执行过程如图3-14所示。
图3-14 shell命令基本执行过程
以上介绍的仅是shell作为命令解释程序的基本工作原理,其实际工作过程是很复杂的。既便如此,我们从中也可以体会到:进程是动态活动的,父子进程间构成族系,彼此间有同步关系,进程间的切换是由进程调度程序实现的。大家如果有兴趣的话,可结合上机实习,深入想一想命令的执行过程。
电脑