操作系统内存管理


物理内存


Linux为了有效使用机器中的物理内存,在系统初始化阶段内存被划分成几个功能区域:

操作系统内存管理_第1张图片


其中,Linux内核程序占据在物理内存的开始部分,接下来是供硬盘等块设备使用的高速缓冲区部分(其中要扣除显卡内存和ROM BIOS所占用的内存地址范围)

当一个进程需要读取块设备中的数据时,系统会首先把数据读到高速缓冲区中。当有数据需要写到块设备上去时,系统也是先将数据放到高速缓冲区中,然后由块设备驱动程序写到相应的设备上。

内存的最后部分是可供所有程序随时申请和使用的主内存区。内核在使用主内存区时,首先要向内核内存管理模块提出申请,并在申请成功后方能使用。

 

在Intel 80386及以后的CPU中提供了两种内存管理(地址变换)系统:内存分段系统分页系统。其中分页管理系统是可选择的,由系统程序员通过编程来确定是否采用。Linux同时采用了分段和分页机制。

 



内存地址空间概念


在Linux内核中,在进行地址映射操作时,我们要分清3种地址的概念:进程的虚拟(逻辑)地址、CPU的线性地址、实际的物理地址。


虚拟地址


虚拟地址是指由程序产生的段选择子和段内偏移地址两部分组成的地址。

(因为这两部分组成的地址并没有直接用来访问物理内存,而是需要通过分段地址变换机制或映射后才对应到物理内存地址上,故被称为虚拟地址。)

虚拟地址空间GDT映射的全局地址空间和由LDT映射的局部地址空间组成。

【关于GDT和LDT,请移步-<CPU的实模式与保护模式>】

 

Intel 80x86 CPU可以索引16384个选择符。若每个段的长度都取最大值4GB,则最大虚拟地址空间范围是16384*4GB=64TB

 

CPU的内存管理给程序员提供了这样一个抽象的内存模型:

操作系统内存管理_第2张图片


程序员(无论是汇编的还是高级语言的)可以把内存分布看做是如上图所示,可以认为内存中只有自己的程序,自己独占CPU

这是硬件和操作系统一起提供给程序员的简单抽象。

(底层的实现:地址变换、任务切换等对程序员是透明的)

 

逻辑地址是指由程序产生的与段相关的偏移地址部分。在Intel保护模式下即是指程序执行代码段限长内的偏移地址

应用程序员仅需与逻辑地址打交道,而分段和分页机制对他来说完全透明的。(由加载器来设置分段)

Linux 0.12给每个进程都划分了容量为64MB的虚拟内存空间。因此出现的逻辑地址范围是0x0000000到0x4000000。(即设置一个LDT所管辖的范围为64MB)

(现在的操作系统给进程划定的虚拟内存是4GB,当然也可以更多。)

有些资料不区分逻辑地址和虚拟地址的概念,统称逻辑地址。

 

线性地址


线性地址是虚拟地址到物理地址变换之间的中间层,是CPU可寻址的内存空间中的地址

程序代码会产生逻辑地址(或者说是段中的偏移地址),加上相应段的基地址就生成了一个线性地址。(一个段最大有4G的虚拟地址空间,在加载器把程序加载时,很容易选择一个空闲的段,填好GDT和LDT)

如果启用了分页机制,那么线性地址可以再经变换产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。

Intel 80386的线性地址空间容量为4GB。

 

物理地址


物理地址是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。

 


内存分段机制


在实模式下,寻址一个内存地址主要是使用段基址和偏移值,段基址被存放在段寄存器中(如ds),并且段的长度被固定为64KB。段内偏移地址存放在任意一个可用于寻址的寄存器中(如si)。

操作系统内存管理_第3张图片

 

在保护模式下,段寄存器中存放的不再是被寻址段的基地址,而是一个段描述符表中某一描述符项在表中的索引值。索引值指定的段描述符项中含有需要寻址的内存段的基地址、段的长度值、段的访问特权级等信息。(这样通过段描述符访问段中的信息,可检查安全性,则就是所谓的保护模式)

 

这样,在保护模式下寻址一个内存地址就需要比实模式下多一个环节,即需要使用段描述符表

注意,如果你不在一个段描述符中定义一个内存线性地址空间区域,那么该地址区域就完全不能被寻址,CPU将拒绝访问该地址区域。

操作系统内存管理_第4张图片


保存描述符项的描述符表有3种类型,每种用于不同目的。

IDT中断描述符表(Interrupt Descriptor Table),该表保存了定义中断或异常处理过程的段描述符。IDT表直接替代了8086系统中的中断向量表。(使得在跳转到中断程序时也进行权限检查。)

GDT全局描述符表(Globle Descriptor Table),该表可被所有程序用于引用访问一个内存段。

LDT局部描述符表(Local Descriptor Table),通常每个任务使用一个LDT表。每个LDT表为对应任务提供了更多的可用描述符项,因为也为每个任务提供了可寻址内存空间的范围

 

这些表可以保存在线性地址空间的任何地方。为了让CPU能定位GDT表、IDT表和当前的LDT表,需要为CPU分别设置GDTRIDTRLDTR三个特殊寄存器。

 

操作系统内存管理_第5张图片

可以看出,每个任务的局部描述符表LDT本身也是由GDT中描述符定义的一个内存段,在该段中存放着对应任务的代码段和数据段描述符,因此LDT段很短。同样,每个任务的任务状态段TSS也是由GDT中描述符定义的一个内存段。(TSS用于在任务切换时CPU自动保存或恢复相关任务的当前执行上下文)

 


内存分页管理


内存分页管理机制的基本原理是将CPU整个线性地址内存区域划分成4KB为1页的内存页面。程序申请使用内存时,系统就以内存页为单位进行分配。

为了在80x86保护模式下使用分页机制,需要把控制寄存器CR0的最高位置位

 

操作系统内存管理_第6张图片


对于Intel 80386系统,其CPU可以提供多达4GB的线性地址空间。

 

 

操作系统的内存管理(以下为一家之言,仅供参考)

有两套对内存管理的方式:

一、以Linux 0.12 内核为例(点我)

二、以Linux 2.x 内核为例(点我)





你可能感兴趣的:(操作系统内存管理)