您的位置:首页 > 运维架构 > Linux

深入介绍Linux内核(八)

2007-05-24 23:16 363 查看
第五章



Linux内核体系结构

在本章这里,我们首先要介绍的是Linux內核的编制模式和体系结构,然后详细描述了Linux內核原始码目錄中组织形式和子目錄中各个代码档的主要功 能,以及基本呼叫的层次关系等。接着就是直接走入正题,从內核原始档案Linux/目錄下的第一个档Makefile开始,对每一行代码进行详细注释說 明。本章內容可以看作是对內核原始码的总结概述,也可以作为閱读后续章节的参考资讯。对於较难理解的地方可以先跳过,待閱读到后面相关內容时再返回来参考 本章內容。在閱读本章之前请先复习或学习有关80X86保护模式执行方式工作原理。

一个完整可用的作业 充主要由4部分组成:硬体、作业系统內核、作业系统服务和用戶应用程式,图5-l所示。用戶应用程式是指那些字处理程式、Internet浏览器程式或用 户自行编制的各种应用程式;作业系统服务程式是指那些向用戶提供的服务被看作是作业系统部分功能的程式。在Linux作业系统上,这些程式包括X视窗系 统、shell命令解释系统以及那些內核程式设计介面等系统程式;作业系统內核程既是本篇文章所感兴趣的部分,它主要用於对硬件资源的抽象和存取调度。



在汇流排控制器控制下,8259A晶片可以处于程式设计状态和操作状态,程式设计伏态是CPU使用IN或OUT指令对82 59A晶片进行初始化程式设计的状态。一旦完成了初始化程式设计,晶片即进入操作状态,此时晶片即可随时回应外部装置提出的中断请求(IRQ0 - IRQ15),同时系统还可以使用操作命令字随时修改其中断处理方式。透过中断判优选,晶片将选中当前最高优先顺序的中断清求作为中断服务对象,並透过 CPU接脚INT通知CPU外中断请求的到来,CPU回应后,晶片从资料汇流排D7-D0将程式设计设定的当前服务对象的中断号送出,CPU由此获取对应 的中断向量值,並执行中断服务程式。

5.4.3 中断向量表

上节已指出CPU是根据中断号获取中断向量值,即对应中断服务程式的入口位址直。因此为了让CPU由中断号查找到对应得中断向量,就需要在记忆体中建立一 张查询表,即中断向量表(在32位元保护模式下该表称为中断描述符表,见下面說明)。80X86微机支持256个中断,对应每个中断需要安排一个中断服务 程式。在80X86真实模式执行方式下,每个中断向量由4个位元组组成。这4个立元组指明了一个中断服务程式的段值和段內偏移值。因此整个向量表的长度为 1024位元组。当80X86微机啟动时,ROM BIOS中的程式会在实体记
忆体开冶位址0x0000 : 0x0000处初始化並设置中断向量表,而各中断的预设中断服务程式则在BIOS中给出。由於中断向量表中向量是按中断号顺序排列,因此给定一个中断号 N,那麼它对应的中断向量在记忆体中的位置就是0x0000 : N*4,即对应的中断服务程式入口位保存在实体记忆体0x0000 : N*4位置处。

在BIOS执行初始化操作时,它设置了两个8259A晶片支援的16个硬体中断向量和BIOS提供的中断号为0x10 - 0xlf 得中断呼叫功能向量等。对於实际沒有使习的向量则填入临时的哑中断服务程式位址。以后在系统开机载入作业系统时会根据实际需要修改某些中断向量的值 列如,对於DOS作业系统,它会重新役置中断0x20 – 0x2f的中断向量值。而对於Linux系统,除了在刚开始载入內核时需要用到BIOS提供的显示和磁碟读取操作中断功能,在內核正常执行之前则会在 setup.s程式中重新初始化8259 A晶片並且在head.s程式中重新设置一张中断向量表(中断描述符表)。完全拋弃BIOS所提供的中断服务功能。

当Intel CPU执行在32位元保护模式下时,需要使用中断描述符表IDT (Interrupt Descriptor Table)来管理中断或异常。IDT是Intel 8086- -80186CPU中使用的中断向量表的直接替代物。其作用也类似于中断向量表,只是其中每个中断描述符项中除了含有中断服务程式位址以外,还包含有关特 权级和描述符类別等资讯。Linux作业系统工作於80X86的保护模式下,因此它使用中断描述符表安来设置和保存各中断的“向量”资讯。

5.4.4Linux內核的中断处理

於于Linux內核来說,中断信号通常分两类 :硬体中断和软件中断(異常)。每个中断是由 0-255 之间的一个数字来标识。对於中断int0- -int3l(0x00- -0xff),每个中断的功能由Intel公司固定设定或保留用,属於软体中断,但Intel公司称之为異常。因为这些中断是在CPU执行指令时探测到異 常晴況而引起的。通常还可分为故障(Fault)和陷阱(traps)两类。中断
int32- -int255(0x20- -0xff)可以由用戶自己设定。所有中断的分类以及执行后CPU的动作方式见表5-1所示。

Linux內核的主要用途就是为了与电脑硬件进行交流,实现对硬件部件的程式设计控制和介面操作,调度对硬件资源的存取,並为电脑上的用戶程式提供一个高级的执行环境和对硬件的虚拟介面。

在本章內容中,我们是基于Linux 0.12版的內核原始码,简明地描述Linux內核的基本体系结构、主要构成模组。然后对原始码中出现的几个重要资料结构进行說明。最后描述了建构Linux 0.12 內核编译实验环境的方法。

5.1 Linix內核模式

目前,作业系统內核的结构模式主要可分为整体式的单內核模式和层次式的微內核模式。而本文章所注释的Linux 0.12內核,则是採用了单內核模式。单內核模式的主要优点是內核代码结构紧湊,执行速度快,不足之处主要是层次结构性不強。

在单內核模式的系统中,作业系统所提供服务的流程为:应用主程序使用指定的参数值执行系统呼叫指令(int x80),使CPU从用戶态(User Mode)切換到核心态(Kernel Model),然后作业系统根据具体的参数值呼叫特定的系统呼叫服务程式,而这些服务程式则根据需要再呼叫底层的一些支援函数以完成特定的功能。在完成了 应用程式所要求的服务后,作业系统又使CPU从核心态切換回用戶态,从而返回到应用程式中继续执行后面的指令。因此概要地讲,单內核模式的內核也可粗略地 分为三个层次:呼叫服务的主程序层、执行系统呼叫的服务层和支援系统呼叫的底层函数。见图5-2所示。



5.2 Linux系统体系结构

Linux內核主要由5个模组构成,它们分別是:行程调度模组、记忆体管理模组、档案系统模组、行程间通信模组和网路介面模组。

行程调度模组用来负责控制行程对CPU资源的使用。所採取的调度策略是各行程能夠公平合理地存取CPU,同时保证內核能及时地执行硬件操作,记忆体管理模 组用於确保所有行程 能夠安全地共用机器主记忆体区,同时,记忆体管理模组还支援虛拟记忆体管理方式,使得Linux支援行程使用比实际记忆体空间更多的记忆体容量。並可以利 用档案系统把暂时不用的记忆体资料块交換到外部储存装置上去,当需要时再交換回来。档案系统模组用於支援对外部装置的驱动和储存。虛拟档案系统模组透过向 所有的外部储存装置提供一个通用的档介面,隐藏了各种硬件装置的不同细节。从而提供並支援与其他作业系统相容的多种档案系统格式。行程间通信模块子系统用 於支援多种行程间的资讯交換方式。网路介面模块提供对多种网络通信标準的存取並支援许多网路硬体。

这几个模块之间的依赖关系见图5-3所示。其中的连線代表它们之间的依赖关系,虛線和虛框部分表示Linux 0.12中还未实现的部分(从Linux 0.95版才开始逐步实现虛拟档案系统,而网路介面的支援到0.96版才有)。



由图可以看出,所有的模组都与行程调度模组存在依赖关系。因为它们都需要依靠行程调度程式来挂起(暂停)或重新执行它们的行程。通常,一个模组会在等待硬 件操作期间被挂起,而在操作完成后才可继续执行。例如,当一个行程试图将一资料块写到软碟上去时,软碟驱动程式就可能在啟动软碟旋转期问将该行程置为掛起 等待状态,而在软碟进入到正常转速后再使得该行程能继续执行。另外3个模组也是由於类似的原因而与行程调度模组存在依赖关系。

其他几个模组的依赖关系有些不太明显,但同樣也很重要。行程调度子系统需要使用记忆体管理来调整一特定行程所使用的实体记忆体空间。行程间通信子系统则需 要依靠记忆体管理器来支援共用记忆体通信机制。这种通信机制允许两个行程存取记忆体的同一个区域以进行行程间资讯的交換。虛拟档案系统也会使用网路介面来 支援网路档案系统(NFS) ,同樣也能使用记忆体管理子系统提供记忆体虛拟碟(ramdisk)装置。而记忆体管理子系统也会使用档案系统来支援记忆体资料块的交換操作。

若从单內核模式结构模型出发,我们还可以根据Linux 0.12內核原始码的结构将內核主要模组绘制成图5-4所示的框图结构。



其中內核级中的几个方框,除了硬体控制方框以外,其他粗線方框分別对应內核原始码的目錄组织结构。

除了这些图中已经给出的依赖关系以外,所有这些模组还会依赖於內核中的通用资源。这些资源包括內核所有子系统都会呼叫的记忆体分配和收回函数、列印警告或出错非讯函数以及一些系统除错函数。

5.3 Linux內核对记忆体的管理和使用

本节首先说明Linux 0.12系统中比较直观的实体记忆体使用情況,然后结合Linux 0.12內核中的应用情況,再分別概要描述记忆体的分段和分页管理机制以及CPU多工操作和保护方式。最后我们再综合说明Linux 0.12系统中內核代码和资料以以及各个任务的代码和资料在虛拟位址、线性位址和实体位址之间的对应关系。

5.3.1实体记忆体

在linux0.12內核中,为了有效地使用机器中的实体记忆体,在系统初始化阶段记忆体被划分成几个功能区域,见图5-5所示



其中,Linux內核程式佔据在实体记忆体的开始部分,接下来是供硬碟或软碟等区块装置使用的高速缓冲区部分(其中要扣除显示卡记忆体和ROM BIOS所佔用的记忆体位址范围640K- -l MB) 。当一个行程子要读取区块装置中的数据时,系统会首先把数据读到高速缓冲区中;当有数据需要写到区块装置上去时,系统也是先将数据放到高速缓冲区中,然后 由区 块装置驱动程式写到相应的装置上。记忆体的最后部分是供所有程式可以随时申请和使用的主记忆体区,內核程式在使用主记忆体区时,也同樣首先要向內核记忆体 管理模组提出申请,並在申请成功后才能使用。对於含有RAM虛拟碟的系统,主记忆体区头部还要划去一部分,供虚拟盘存放数据。

由于电脑系统中所含的实际实体记忆体容有限,因此CPU中通常都提供了记忆体管理机制对系统中的记忆体进行有效的管理。在Intel 80386及以后的CPU中提供了两种记忆体管理(位址变換)系统:记忆体分段系统(Segmentation Systen)和分页系统(Paging System)。其中分页管理系统是可选择的,由系统程式师透过程式设计来确定是否採用。为了能有效地使用实体记忆体,Linux系统同寺採用了记忆体分 段和分页管理机制。

5.3.2 记忆体位址空间概念

Linux0.12內核中,在进行位址映射操作时,我们需要首先分清3种位址以及它们之间的变換概念:a.程式(行程)的虚拟和逻辑位址:b.CPU的線性位址:C.实际实体记忆体地址。

虚拟位址(Virtual Address)是指由程式产生的由段选择符和段內偏移位址两个部分组成的位址。因为这两部分组成的位址並沒有直接用来存取实体记忆体,而是需要透过分段 位址变換机制处理或映射后才对应到实体记忆体位址上,因此这种位址被称为虛拟位址。虛拟位址空间 GDT映射的全域位址空间和由LDT映射的区域位址空间组成。选择符的索引部分由13个Bit位表示,加上区分GDT和LDT的l个Bit位,因此 Intel 80X86 CPU共可以索引16384个选择符。若每个段的长度都取最大值4G,则最大虚拟位址空间范围是16384*4G=64T。

逻辑位址(Logical Address)是指由程式产生的与段相关的偏移位址部分。在Intel保护模式下即是指程式执行代码段限长的偏移位址(假定代码段、资料段完全一樣)。 应用程式师仅需与逻辑位址打交道,而分段和分页机制对他来說是完全透明的,仅由系统程式设计人员涉及 。不过有些资料並不区分逻辑位址和虛拟位址的概念,而是将它们统称为逻辑位址。

线性位址(Linear Address)是虛拟位址到体位址变換之间的中间层,是处理器可定址的记忆体空间(称为線性位址空间)中的位址。程式码会產生逻辑位址,或者說是段中的 偏移位址,加上相应段的基底位址就生成了一个線性位址。如果啟用了分页机制,那麼線性位址可以再经变換以產生一个实体位址。若沒有啟用分页机制,那麼線性 位址直接就是实体位址。Intel 80386的線性位址空间容良为4G。

实体位址(Physical Address)是指出现在CPU外部位址汇流排上的定址实体记体的位址信号,是位址变換的最终结果地址。如果啟用了分页机制,那么線性位址会使用页目录和页表中的项变換成实体位址。如果沒有啟用分员机制,那麼線性位址就直接成为实体位址了。

虛拟储存(或虛拟记忆体) (Virtual Memory)是指电脑呈现出要比实际拥有的记忆体大得多的记忆体量。因此它允许程式师编制並执行比实际系统拥有的记忆体大得多的程式。这使得许多大型专 案也能夠在具有有限记忆体资源的系统上实现。一个很恰当的的比喻是:你不需要很长的轨道就可以让一列火车从上海开到北京。你只需要足夠长的铁轨(比如說3 公里)就可以完成这个任务。採取的方法是把后面的铁轨立刻铺到火车的前面,只要你的操作足夠快並能满足要求,列车就能象在一条完整轨道上执行。这也就是虛 拟记忆体管理需要完成的任务。在Linux 0.12內核中,给每个程式(行程)都划分了总容量为64MB的虛拟记忆体空间。因此程式的逻辑位址范围是Ox0000000到0x4000000。

如上所述,有时我们也把逻辑位址称为虛拟位址。因为逻辑位址与虛拟记忆体空间的概念类似,并且是与实际实体记忆体容量无关。

5.3.3 记忆体分段机制

在记忆体分段系统中,一个程式的逻辑位址透过分段机制自动地映射(变換)到中间层的4GB (2³²)线性位址空间中。程式每次对记忆体的引用都是对记忆体段中记忆体的参照引用。程 式引用一个记忆体位址时,透过把相对应的段基址加到程式师看得见的逻辑位址上就形成了一个对应的線性位址。此时若沒有啟用分页机制,则该線性位址被送到 CPU的外部位址汇流排上,用於直接定址对应的实体记忆体。见图5-6所示。



图5-6 虚拟位址(逻辑位址)到实体位址的变换过程



CPU进行位址变换(映射)的主要目的是为了解決虛拟记忆体空间到实体记忆体空间的映射问题。虚拟记忆体空间的含义是指一种利用二级或外部储存空间,使程式能不受实际实体记忆体量限制而使用记忆体的一种方法。通常虛拟记忆体空间要比实际实体记忆体量大得多。

那麼虛拟储存管理是怎样实现的呢? 原理与上述列车运行的比喻类似。首先,当一个程式需要使用一块不存在的记忆体时(也即在记忆体页表项中已标出相应记忆体页面不在记忆体中),CPU就需要 一种方法来得知这个情況。这是透过80386的页错误異常中断来实现的。当一个行程引用一个不存在页面中的记忆体位址时,就会触发CPU产生页出错異常中 断,並把引起中断的線性位址放到CR2控制暂存器中。因此处理该中断的过程就可以知道发生页異常的确切位址,从而可以把行程要求的页面从二级储存空间(比 如硬碟上)载入到实体记忆体中。如果此时实体记忆体已经全部佔用,那麼可以借助二级储存空间的一部分作为交換缓冲区(Swapper)把记忆体中暂时不使 用的页面交換到二级缓冲区中,然后把要求的页面调入记忆体中。这也就是记忆体管理的缺页载入机制,在Linux 0.12內核中是在程式mm/memory.c中实现。

Intel CPU使用段(Segment)的概念来对程式进行定址。每个段定义了记忆体中的某个区域以及存取的优先顺序等资讯。假定大家知晓真实模式下记忆体定址原 理,现在我们根据CPU真实模式和保护模式下定址方式的不同,用比较的方法来简单說明32位元保护模式执行机制下记忆体定址的主要特点。

在真实模式下,定址一个记忆体位址主要是使用段和偏移值,段值被存放在段寄存器中(例如ds) ,並且段的长度被固定为64KB。段內偏移位址存放在任意一个可用於定址的寄存器中(例如si)。因此,根据段寄存器和偏移寄存器中的值,就可以算出实际 指向的记忆体位址,见图5-7(a)所示。

而在保护模式执行方式下,段寄存器中存放的不再是被定址段的基底位址,而是一个段描述符表(Segment Descriptor Table)中某一描述符项在表中的索引值。索引值指定的段描述符项中含有需要定址的记忆体段的基底位址、段的长度值和段的存取特权级別等信息。定址的记 忆体位置是由该段描述符项中指定的段基底位址值与一个段內偏移值组合而成。段的长度可变,由描述符中的內容指定。可见,和真实模式下的定址相比,段寄存器 值換成了段描述符表中相应段描述符的索引值以及段表选择位和特权级,称为段选择符(Segment Selector) ,但偏移值还是使用了原真实模式下的概念。这樣,在保护模式下定址一个记忆体位址就需要比真实模式下多一道手续,也即需要使用段描述符表。这是由於在保护 模式下存取一个记忆体段需要的资讯比较多,而一个16位的段寄存器放不下这麼多內容。示意图见图5-7 (b)所示。注意,如果你不在一个段描述符中定义一个记忆体線性位址空间区域,那么该位址区域就完全不能被定址,CPU将拒絕存取该位址区域。



每个描述符佔用8个位元组,其中含有所描述段在線性位址空间中的起始位址(基址) ,段的长度、段的类型(例如代码段和资料段) 、段的特权级別和其他一些资讯。一个段可定义的最大长度是4GB。

保存描述符项的描述表有3种类型,每种用於不同目的。全域描述符表GDT(Global Descriptor Table)是主要的基本描述符表,该表可被所有程式用於参照引用存取一个记忆段。中断描述符表IDT(Interrupt Descriptor Table)保存有定义中断或異常处理过程的段描述符。IDT表直接替代了8086系统中的中断向量表。为了能在80X86保护模式下正常执行,我们必须 为CPU定义一个GDT表和一个IDT表。最后一种类型的表是区域描述符表LDT(Local
Descriptor Table)。该表应用於多工系统中,通常每个任务使用一个LDT表。作为对GDT表的扩充,每个LDT表为对应任务提供了更多的可用描述符项,因而也为 每个任务提供了可定位记忆体空间的范围。这些表可以保存在線性位址空间的任何地方。为了让CUP能定位GDT表、IDT表和当前的LDT表,需要为CPU 分別设置GDTR、IDTR和LDTR三个特殊寄存器。这些寄存器中将储存对应表的32位元線性基底位址和表的限长位元组值。表限长值是表的长度值-1。

当CPU要定址一个段时,就会使用16位的段寄存器中的选择符来定位一个段描述符。在80X86 CPU中,段寄存器中的值右移3位即是描述符表中一个描述符的索引值。13位元的索引值最多可定位8192(0- -8191)个的描述符项。选择符中位元2 (TI)用来指定使用哪个表。若该位是0则选择符指定的是GDT表中的描述符,否则是LDT表中的描述符。

每个程式都可有若干个记忆体段组成。程式的逻辑位址(或称为虛拟位址)即是用於定址这些段和段中具体位址位置。在Linux 0.12中,程式逻辑位址到線性位址的变換过程使用了CPU的全域段描述符表GDT和区域段描述符表LDT 。由GDT映射的位址空间称为全域位址空间,由LDT映射的位址空间则称为区域位址空间,而这两者构成了虛拟位址的空问。具体的使用方式见图5-8所示。



图中画出了具有两个任务时情況。可以看出,每个任务的区域描述符表LDT本身也是由GDT中描述符定义的一个记忆体段,在该段中存放著对应任务的代码段和 资料段描述符,因此LDT段很短,其段限长通常只要大於24位元组即可。同樣,每个任务的任务状态段TSS也是由GDT中描述符定义的一个记忆体段,其段 限长也只要满足能够存放一个TSS资料结构就夠了。

对於中断描述符表idt,它保存在內核代码段中。由於在Linux 0.12內核中,內核和各任务的代码段和资料尽都分別被映射到線性位址空间中相同基址处,且段限长也一樣,因此內核的代码段和资料段是重疊的,各任务的代 码段和资料段分別也是重疊的,参见图5-10 图5-11所示。任务状态段TSS(Task State Segment)用於在任务切換时CPU自动保存或恢复相关任务的当前执行上下文(CPU当前状态) 。例如对於切换出的任务,CPU就把其暂存器等资讯保存在该任务的TSS段中,同时CPU 目新切換进任务的TSS段中的资讯来设置各寄存器,以恢复该任务的执行环境,参见图4-37所示。在Linux 0.12中,每个任务的TSS段內容被保存 在该任务的任务资料结构中。另外,Linux 0.12內核中沒有使用到系统段描述符。

5.3.4 记忆体分页管理

若採用了分页机制,则此时線性位址只是一个中间结果,还需要使用分页机制进行变換,再最终映射到实际实体记忆体位址上。与分段机制类似,分页机制允许我们 重新定向(变换) 每次记忆体引用,以适应我们的特殊要求。使用分页机制最普遍的场合是当然记忆体实际上被分成很多凌乱的区块时,它可以建立一个大而连续的记忆体空间映射, 好让程式不用操心和管理这些分散的记忆体区块。分页机制增強了分段机制的效能。另外,页位址变換建立在段变換基础之上,任何分页机制的保护措施不会取代段 变換的保护措施而只是进行更进一步的检查操作。

记忆体分页管理机制的基本原理是将CPU整个線性记忆体区域划分成4096位元组为1页的记忆体页面。程式申请使用记忆体时,系统就以记忆体页为单位进行 分配。记忆体分页机制的实现方式与分段机制很相似,但並不如分段机制那麼完善。因为分页机制是在分段机制之上实现的,所以其结果是对系统记忆体具有非常灵 活的控制权,并且在分段机制的记忆体保护上更增加了分页保护机制。为了在80X86保护模式下使用分页机制,需要把控制寄存器CR0的最高Bit位(位 31)置位。

在使用这种记忆体分页管理方法时,每个执行中的行程(任务)可以使用此实际记忆体容量大得多的连续位址空间。为了在使用分页机制的条件下把線性位址映射到 容量相对很小的实体记忆体空间上,80386使用了页目錄表和页表。页目錄表项与页表项格式基本相同,都佔用4个位元组,並且每个页目錄表或页表必须只能 包含1024个页表项。因此一个页目錄表或一个页表分別共佔用l页记忆体。页目錄项和页表项的小区別在於页表项有个已写D (Dirty),而页目錄则没有。

線性位址到实体位址的变換过程见图5-9所示。图中控制寄存器CR3保存著是当前页目錄表在实体记忆体中的基底位址(因此CR3也被称为页目錄基底位址暂 存器PDBR)o。32 元的線性位址被分成三个部分,分別用来在页目錄表和页表中定位对应的页目录项和页表项以及在对应的实体记忆体页面中指定页面內的偏移位置。因为l 个页表可有1024项,因此一个页表最多可以映射1024*4KB = 4MB记忆体; 因为一个页目錄表最多有1024项,对应1024个二级页表,因此一个页目錄表最多可以映射1024*4MB = 4GB容量的记忆体。即一个页目錄表就可以映射整个线性位址空间范围。

由於Linux 0.1x系统中内核和所有任务都共用同一个页目錄表,使得任何时刻处理器線性位址空间到实体位址空间的映射函数都一樣。因此为了让內核和所有任务都不互相 重疊和干扰 ,它们都必须从虛拟位址空间映射到線性位址空间的不同位置,即佔用不同的线性位址空间范围。



对於Intel 80386系统,其CPU可以提供多达4G的線性位址空间。一个任务的虛拟位址需要首先透过其区域段描述符变換为CPU整个線性位址空间中的位址,然后再 使用页目錄表PDT (一级页表)和页表PT (二级页表)映射到实际实体位址页上。为了使用实际实体记忆体,每个行程的线性位址透过二级记忆体页表动态地映射到主记忆体区域的不同实体记忆体页上。由 於Linux 0.12中把每个行程最大可用虛拟记忆体空间定义为64MB,因此每个行程的逻辑位址透过加上(任务号)*64MB,即可转换为线性空间中的位址。不过在 注释中,在不至於搞混的情況下我们有时将行程中的此类位址简单地称为逻辑位址或線性位址。

对於Linux 0.12系统,內核设置全域描述符表GDT中的段描述符项数最大为256,其中2项空间、2项系统使用,每个行程使用两项。因此,此时系统可以最多容纳 (256-4)/2 = 126个 任务,並且虛拟位址范围是((256-4)/2)*64MB約等於8G。但0.12內核中人工定义最大任务数NR_TASKS = 64个,每个任务逻辑位址范围是64M,並且各个任务在线性位址空间中的起始位置是(任务号)*64MB。因此全部任务所使用的线性位址空间范围是 64MB*64 = 4G,见图5-10所示。图中示出了当系统具有4个任务时的情況。內核代码段和资料段被映射到线性位址空间的开始16MB部分,並且代码和资料段都映射到 同一个区域,完全互相重疊。而第1个任务(任务0)是由內核“人工”啟动执行的,其代码和资料包含在內核代码和资料中,因此该任务所佔用的线性位址空间范 围比较特殊。任务0的代码段和资料段的长度是从線性位址0开始的640KB范围,其代码和资料段也完全重疊,并且与內核代码段和资料段有重疊的部分。实际 上,Linux 0.12中所有任务的指令空间I (Instruction)和资料空间D (Data)都合用一块记忆体,即一个行程的所有代码、资料和堆疊部分都处於同一记忆体段中,也即是I&D不分离的一种使用方式。

任务1的線性位址空间范围也只有从64MB开始的640KB长度。它们之间的详细对应关系见后面說明。任务2和任务3分別被映射線性位址128MB和 192MB开始的地方,並月它们的逻辑位址范围均是64MB 。由於4G位址空间范围正好是CPU的线性位址空间范围和可定址的最大实体位址空间范围,而且在把任务0和任务l的逻辑位址范围看作64MB时,系统中同 时可有任务的逻辑位址范围总和也是4GB,因此在0.12內核中比较容易混淆三种位址概念。



如果也按照线性空间中任务的排列顺序排列虛拟空间中的任务,那麼我们可以有图5-11所示的系统I 同时可拥有所有任务在虛拟位址空间中的示意图,所佔用虛拟空间范围也是4GB (中沒有考虑內核代码和资料在虛拟空间中所佔用的范围。另外,在图中对於行程2和行程3还分別给出了各自逻辑空间中代码段和资料段(包括资料和堆栈内容) 的位置示意图。



我们还需要注意的,是行程逻辑位址空间中代码段(Code Section)和资料段(DataSection)的概念与CPU分段机制中的代码段和资料段不是同一个概念。CPU分段机制中段的概念确定了在线性位 址空间中一个段的用途以及被执行或存取的約束和限制,每个段可以设置在4GB线性位址空间中的任何地方,它们可以相互独立也可以完全重疊或部分重叠。而行 程在其逻辑位址空间中的代码段和资料段则是指由编译器在编译程序和作业系统在载入程式时规定的在行程逻辑空间中顺序排列的代码区域、初始化和未初始化的资 料区域以及堆栈区域。行程逻辑位址空间中代码段和资料段等结构形式见图所示。有关逻辑位址空间的說明请参阅记忆体管理一章內容。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: