没有合适的资源?快使用搜索试试~ 我知道了~
Linux内核情景分析之三中断和函数调用
需积分: 9 10 下载量 102 浏览量
2009-11-10
22:10:32
上传
评论
收藏 137KB DOC 举报
温馨提示
试读
16页
Linux内核情景分析之三中断和函数调用 Linux内核情景分析之三中断和函数调用
资源推荐
资源详情
资源评论
Linux 内核情景分析
前言
本书着重于对 Linux 系统最新版本( 2.4.0)内核源代码进行情景描述和情景分析。
什么是情景描述?什么是情景分析?
不妨以英语的教学为例。大家都知道,有·种很有效的方法是通过“情景会话”学习英语。例如,
去剧院问路要说些什么,去图书馆借书要说些什么,去餐馆吃饭碰上了熟人又说些什么,等等。每-
个这样的“情景”都是个常见或常用的会话过程。以这样的一些情景为线索,沿着这些线索讲解“这
足被动语态”、“那足习惯用法”,就容易引起学习人的兴趣从而印象深刻,并目.每学了这样一个情景就
能够实际运用。另外,f}3 于来自现实生活的情景在语法、语义等方面都不是单一的,在学习一个情景
的时候通常都会涉及该语言种种小 1 司的方面,通过一系列精心安排的情景会话的学习,就能对英语逐
步地建立起比较全面的认识。事实上,就英语的学习 IfrJ 言,纯粹的系统化学习方法儿乎是不现实的。
事实上,指以少有人通过读字典来学单词,而都是结合课文来学,侮篇课文实际上也是一个情景。当然,
系统化的学习还是要的,学了情景对话以后还要再系统地学习语法。但是无可否认的是,从清景对话
入手学习英语比从语法入手要有效得多。相信读者会有这方面的体会和经历。
现在来看对 Linux 内核的学习。如果以若十经过精心安排的情景为线索,例如,打开一个文件的
全过程,执行一个可执行程序的全过程,从一个进程发送一个报文到另一个进程的过程等等,结合内
核源代码逐个加以讲解,夕卜且在讲解过程中有针对性地介绍所涉及的数据结构和算法,读者就能得到
对整个内核的生动而深刻的理解。本书的宗旨之一就在于引导读者走过许多这样的“情景”,从而建立
起对 Linux 内核的全面的认识。至丁情景的安排,仍然按照操作系统的原理分成若干章,例如存储管
理、进程管理、文件系统等等。在每一章中,除了必要的叙述以外,都挑选了若干重要的情景,结合
源代码逐个加以讲解。
木书所用的源代码,刚开始编写初稿时取自当时最新的 Linux 内核 2.3.38 版,后来历经 2.3.98 和
2.4.0 测试版,最后依据 2.4.0 止式版重新修改定稿。读者可以在相关的网站上自行下载该版内核的全部
源代码。可以肖定,当读者看到本书时,县至本书付印时,最新的版本己不再是 2.4.U 了。但是不管怎
样我们总得要锁定一个版本,这就是 2.4.0
一般情况下,分析操作系统源代码的专著或教材习惯上都是这样安排的:以主要数据结构的定义
为核心,以数据结构之间的联系为线索,内容则以对文件、模块和函数的功能描述为主,辅以若+函
数,},的代码片断作为实例,以达到介绍、分析各种特定机制的目的口这种思路和安排基本上类似于先
讲语法规则后举一些例句的外语教学方法,它比较适合于只要求对内核和它的原理有粗略了解的读者,
但对需要深入理解内核或实际从事这方面工作的读者就未必合适。其实,这种安排对于初学者也未必
第三章:中断和异常
我们假定本书的读者己经具备了计算机系统结构方面的基础知识,所以本章对中断以及异常
(exception)处理的原理和机制不作深入的介绍。缺乏这方面基础的读者不妨先阅读一些微处理器方 面
的有关材料。不过,我们也并不要求读者对相关内容已经具备了很深入的理解。事实上,随着我们的介绍
和分析,特别是随着各个情景的发展和代码的阅读,读者自会逐步地加深理解。
先简要提一下,中断有两种,一种是由 CPU 外部产生的,另一种是由 CPU 本身在执行程序的过程中产
生的。
外部中断,就是通常所讲的“中断”(interrupt)。对于执行中的软件来说,这种中断的发生完全是“异步”
的,根本无法预测此类中断会在什么时候发生。因此,CPU(或者软件)对外部中断的响应完全是被动的。
不过,软件可以通过“关中断”指令关闭对中断的响应,把它“反映情况”的途径掐断,这样就可以眼不见心
不烦了(这里不考虑“不可屏蔽中断”)。
由软件产生的中断则不同,它是由专设的指令,如 X86 中的“INT n",在程序中有意地产生的,所以是
主动的,“同步”的。只要 CPU 执行了一条 INT 指令,就知道在开始执行下一条指令之前一定要先进入中
断服务程序。这种主动的中断称为“陷阱”(trap) o
此外,还有一种与中断相似的机制称为“异常”(exception),一般也是异步的,多半由于“不小心”犯了
规才发生。例如,当你在程序中发出一条除法指令 DIV,而除数为 0 时,就会发生一次异常。这多半是因
为不小心,而不是故意的,所以也是被动的。当然,也不排除故意的可能性。我们在第 2 章中看到过通过
页面异常扩展堆栈区间的情景,那就是故意安排的。
这样,一共就有二种类似的机制,即中断、陷阱以及异常。
但是,不管是外部产生的中断还是陷阱,或者异常,不管是无意的、被动的,还是故意的、主动的,
CPU 的响应过程却基本上一致。这就是:在执行完当前指令以后,或者在执行当前指令的中途,就根据
中断源所提供的“中断向量”,在内存中找到相应的服务程序入口并调用该服务程序。外部中断的向量是由
软件或硬件设置好了的,陷阱的向量是在“自陷”指令中发出的 (INT n 中的 n),而各种异常的向量则是
CPU 的硬件结构中预先规定好的。这样,这些不同的情况就因中断向量的不同而互相区分开来了。因此
衣实践中常常将这些不同的情况作为一种统一的模式加以考虑和实现,而且常常统称为“,中断”。至于系
统调用,一般都是通过 INT 指令实现的,所以也与中断密切相关。
本章前一部分内容讲中断,包括中断的硬件支持、软件处理以及中断响应和服务的过程;后一部分则
介绍系统调用的有关内容。
3.1 X86 CPU 对中断的硬件支持
本节不讨论严格意义上的中断响应全过程(比如说,怎样获得中断向量),而是着重讨论 CPU 在响应
中断时,即在得到了中断向量以后,怎样进入相应的中断服务程序的过程。这是从操作系统的角度需要关
心的问题 o Intel X86 CPU 支持 256 个不同的中断向量,这一点至今未变。可是,早期 X86 CPU 的中
断响应机制是非常原始、非常简单的。
在实地址模式中,CPU 把内存中从 0 开始的 1K 字竹作为一个中断向量表。表中的每个表项占四个字
节,由两个字节的段地址和两个字节的位移组成。这样构成的地址便是相应中断服务程序的入口地址。这
与 16 位实地址模式中的寻址方式也是一致的。但是,在这样的机制上是不能构筑现代意义的操作系统的 ,
即使把 16 位寻址改成 32 位寻址,即使实现了页式存储管理,也还是无济于事。原因在于,这个机制中
并没有提供空间切换,或者说运行模式切换的手段。为了理解这点,让我们来看看其他的 CPU 是怎么做
的。读者也许知道,早期的 UNIX 是在 PDP-11 上实现的。PDP-11 的 CPU 中有一个与 X86 的 FLAGS 寄
存器相类似的控制状态寄存器,称为 PSW(处理器状态字)。PSW 中有一个位段决定了 CPU 的当前运行优
先级和模式(系统或用户)。在用户程序中是不能通过直接修改 PSW 来达到调高优先级的目的的。在 PDP-
11 的中断向量表中,每个表项由两部分组成,一部分是相应中断服务程序的入口地址,另一部分就是当
CPU 进入中断服务程序后的 PSW。当然,中断向量表的内容只有当 CPU 处于系统模式时才能改变。当中
断发生时,CPL 从向量表中将 PSW 装入其控制状态寄存器,而将中断服务程序的入口地址装入程序计数
器,从而达到既转入了相应的中断服务程序,又从一种运行模式切换到另一种运行模式(或优先级别)的双
重目的。至于原来的 PSW 则随中断返回地址一起被压入堆栈,以便 CPU 从中断服务程序返回时能回到原
来的运行模式。这样,就很自然地实现了运行状态的切换。 CPU 平时处于用户状态,无论是因为外部中
断还是系统调用(由软件产生的中断),或是某种异常,都会通过中断向量表进入系统状态,执行完中断服
务程序后返回时便又恢复原状,回到用户状态。相比之下,我们可以清楚地看到,X86 实地址模式下的
中断响应过程所缺少的就是类似于 PDP-11 对 PSW 的处理。
因此,Intel 在实现保护模式时,对 CPU 的中断响应机制作了大幅度的修改。
首先,中断向量表中的表项从单纯的入口地址改成了类似于 PSW 加入口地址并且更为复杂的描述项,
称为“门”( gate ),意思是当中断发生时必须先通过这些门才能进入相应的服务程序。但是,这样的门并
不只是为中断而设的,只要想切换 CPU 的运行状态,即其优先级别,例如从用户的 3 级进入系统的 0 级,
就都要通过一道门。而从用户态进入系统态的途径也并不只限于中断(或异常,或陷阶),还可以通过子程
序调用指令 CALL 和转移指令 JMP 来达到目的。而且,当中断发生时不但可以切换 CPL 的运行状态并转
入中断服务程序,还可以安排进行一次任务切换(所谓“上下文切换”),立即切换到另一个进程。因此在操
作系统中可以设立一个“中断服务进程(任务)”,每当中断发生时就切换到该进程。
按不同的用途和目的,CPU 中一共有四种门,即任务门(task gate )、中断门(interrupt gate 、陷
阱门(trap gate)以及调用门( call gate)。其中除任务门外其他三种门的结构基本相同,不过调用门并不
是与中断向量表相联系的。
TS5 段选择码的作用和段寄存器 CS, DS 等相似,通过 GDT 或 LDT 指向特殊的“系统段”,},的
一种,称为“任务状态段”Ctask state segment) TSSo TSS 实际上是一个用来保存任务运行“现场’,
的
数据结构,其中包括 CPU 中所有与具体进程有关的寄存器的内容(包含页面目录指针 CR3),还包括
了三个堆栈指针。中断发生时,CPU 在中断向量表中找到相应的表项。如果此一表项是 1 个任务门,
少{-
巨通过了优先级别的检查,CPU 就会将当前任务的运行现场保存在相应的 TSS 中,并将任务门所指
向
的 TSS 作为当前任务,将其内容装入 CPU 中的各个寄存器,从而完成了一次任务的切换。为此目的,
CPU 中又增设了·个“任务寄存器”TA,用来指向当前任务的 TSS。在 Linux 内核中,一个任务就是
一个进程,但是进程的“控制块”,即 task_struct 结构,1,需要存放更多的信息。所以,从这个意
义上讲,
Linux 的进程义并不完全是 Intel 设计意图中的任务。读者后面就会看到,Linux 内核;仁不采用任务
门作
为进程切换的手段。通过任务门切换到一个新的任务少仁不是惟一的途径,例如在程序中也可以用 C
ALL
指令或 rMP 指令通过调用门达到同样的目的。DPL 位段的作用后面还要讨论。
除任务门外,其余三种门的结构基本相同,每个门的大小也都是 64 位,见图 3.2 0
16 位 3 位 5 位 16 位〕6 位
图 3. 2 中断门、陷阱门和调用门结构图
三种门之间的不同之处在于 3 位的类型码。中断门的类型码是 110,陷阱门的类型码是 111.而调
用门的类型码是 lUU。与任务门相比,不同之处主要在十:在仟务门中不需要使用段内位移,因为任
务
门并不指向某一个子程序的入曰,TSS 本身是作为一个段来对待的,而中断门、陷阱门和调用门则都
要指!句一个子程序,所以必须结合使用段选择码和段内位移。此外,仟务门中相刘于 D 标志位的位
置
}几永远是 0}
中断门和陷阱门在使用上的区别不在于 l}l 断是外部产生的或是由 CPU 本身产生的,}fu 是在
于通过
中断门进入中断服务程序时 CPU 会自动将中断关闭,也就是将 CPU 中 EFLAGS 寄存器的 IF 标志位
清
成 @,以防嵌套中断的发生;而在通过陷阱门进入服务程序时则维持}F 标志位不变。这就是中断门和
陷阱门的惟一区别。
不管是什么门,都通过段选择码指向】个存储段。段选择码的作用与普通的段寄存器一样。我们
在第 2 章中讲过,在保护模式下段寄存器的内容并不直接指向一个段的起始地址,而是指向由 GDTR
或 LDT'R 决定的某个段描述表中的一个表项,所以才又称为“段选择码”。至于到底是由 GDTR 还是
由 LDTR 所指向的段描述表,则取决十段选择码中的一个 T1 标志位。在 Linux 内核中,实际上只使
用
全局段描述表 GDT,而局部段描述表 LDT 只是在特殊应用中(主要是 WINE)才使用。对于中断门、
陷阱门和调用门来说,段描述表中的相应表项显然应该是一个代码段描述项。而任务门所指向的描述
项,则是专门为 TSS 而设的 TSS 描述项。TSS 描述项的结构一与我们在第 2 章中所讲的基本卜是相
同的,
但是 bit44 的 S 标志位为 0,表示不是·般的代码段或数据段。
每个段描述项中都有一个 DPL 位段,即“描述项优先级别”位段。当 CPU 通过中断门找到一个
代码段描述项,并进而转入相应的服务程序时,就把这个代码段描述项装入 CPU 中,而描述项的
DPL
就变成 CPL〕的当前运行级别,称为 CPL。这与我们在前面所说的 PDP- X 1 在中断时从向量表中同
时装
入 PS W 和服务程序入 u 地址是一致的。可是,在中断门中也有一个 DPL,那是十什么用的呢?这就
是
要讲到 1386 的保护模式中对运行和访问级别进行检查比对的机制了。
Intel 在 i386CPU 中实现了一套可谓复杂得出奇的优先级别检验机制。我们在这里只根据 Linux
内
核的实现介绍其中一部分。山于 Linux 内核避开了这套机制中最复杂的部分,例如不使用任务门,基
本上也不使用调用门(不过为了兼容性的要求确实支持通过调用门来进入系统调用,但不是主流),冉
说在这里我们只关心对代码段的访问,所以剩下的部分就不太复杂了。
当通过一条 INT 指令进入一个中断服务程序时,在指令中给出一个中断向量。CPU 先根据该向量
在中断向量表中找到一扇门〔描述项),在这种情况下一般总是中断门。然后,就要将这个门的 DPL
与 CPU 的 CPL 相比,CPL 必须小于或等于 DPL,也就是优先级别不低于 DPL,才能穿过这扇门。不
过,如果中断是由外部产生或是因 CPU 异常而产生的话,那就免去了这一层检验。穿过了中断门之
后,
还要进一步将日标代码段描述项中的 DPL 与 CPL 比较,口标段的 DPL 必须小于或等于 CPL。也就是
说,通过中断门时只允许保持或提升 CPU 的运行级别,而不允许降低其运行级别。这两个环节中的
任
何个失败都会产生 1 次全面保护异常(general_protection exception ) a
进入中断服务程序时,CPU 要将当前 EFLAGS 寄存器的内容以及返回地址压入堆栈,返回地址是
由段寄存器 CS 的内容和取指令指针 EIP 的内容共同组成的。如果中断是由异常引起的,则还要将一
个
表示异常原因的出错代码也压入堆栈。进一步,如果中断服务程序的运行级别,也就是目标代码段的
DPL,与中断发生时的 CPL 不同,那就要引起更换堆栈。前面提到过,TSS 结构中除所有常规的寄
存
器内容(包括当前的 SS 和 FSP)外,还有尸个额外的堆栈指针(SS 加 ESP)。这三个额外的堆栈指针
分别用于当 CPU 在目标代码段中的运行级别为 0} 1 以及 2 时。所以,CPU 根据寄存器 TR 的内容
找
到当前 Tss 结构,并根据目标代码段的 DPL,从这 TSS 结构中取出新的堆栈指针(SS 加 ESP),并装
入其堆栈段寄存器 SS 和堆栈指针(寄存器)ESP,达到更换堆栈的目的。在这种情况下,CPU 不但要
将 EFLAGS、返回地址以及出错代码压入堆栈,还要先将原来的堆栈指针也压入堆栈(新堆栈)。示意
图 33 也许有助于理解。
具体到 Linux 内核。当中断发生在用户状态、也就是 CPU 在用户空间中运行时,由于用户态的运
行级别为 3,而在内核中的中断服务程序的运行级别为 0,所以会引起堆栈的更换。也就是说,从用户
堆栈切换到系统堆栈。而当中断发生在系统状态时,也就是当 CPU 在内核中运行时,则不会更换堆
栈。
剩余15页未读,继续阅读
资源评论
zyrtnt
- 粉丝: 2
- 资源: 12
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功