4-4 linux内存管理子系统4-4-1 linux内存管理(参考课件)物理地址:cpu地址总线上寻址物理内存的地址信号,是地址变换的最终结果逻辑地址:程序代码经过编译后,出现在汇编程序中的地址(程序设计时使用的地址)线性地址:又名虚拟地址,32位cpu架构下4G地址空间CPU要将一个逻辑地址转换为物理地址,需要两步:1、首先CPU利用段式内存管理单元,将逻辑地址转换成线性地址;2、再利用页式内存管理单元,把线性地址最终转换为物理地址相关公式:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)(通用的)16位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)线性地址=段寄存器的值×16+逻辑地址的偏移部分物理地址=线性地址(没有页式管理)32位CPU:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)线性地址=段寄存器的值+逻辑地址的偏移部分物理地址<——>线性地址(mapping转换)ARM32位:逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)逻辑地址=段内偏移量(段基地址为0)线性地址=逻辑地址=段内偏移量(32位不用乘以32)物理地址<——>线性地址(mapping转换)************************!!以下都是x86模式下!!********************************* 段式与页式存储管理的比较如下表所示。
段式页式分段由用户设计划分,每段对应一个相应的的程序模块,有完整的逻辑意义。
分页用户看不见,由操作系统为内存管理划分。
段面是信息的逻辑单位页面是信息的物理单位便于段的共享,执行时按需动态链接装入。
页一般不能共享段长不等,可动态增长,有利于新数据增长。
页面大小相同,位置不能动态增长。
二维地址空间:段名、段中地址;段号、段内单元号一维地址空间管理形式上象页式,但概念不同往往需要多次缺页中断才能把所需信息完整地调入内存实现页(段)的共享是指某些作业的逻辑页号(段号)对应同一物理页号(内存中该段的起始地址)。
页(段)的保护往往需要对共享的页面(段)加上某种访问权限的限制,如不能修改等;或设置地址越界检查,对于页内地址(段内地址)大于页长(段长)的存取,产生保护中断。
一、段式管理1.1、16位CPU:(没有页式管理)1.1.1、段式管理的由来:16位CPU内部有20位地址总线,可寻址2的20次方即1M的内存空间,但16位CPU 只有16位的寄存器,因此只能访问2的16次方即64K。
因此就采用了内存分段的管理模式,在CPU内部加入了段寄存器,这样1M被分成若干个逻辑段,每个逻辑段的要求如下:1、逻辑段的起始地址(段地址)必须是16的整数倍,即最后4个二进制位须全是0 (因此不必保存)。
2、逻辑段的最大容量为64K。
段寄存器是为了对内存进行分段管理而增加的,16位CPU有4个段寄存器,程序可同时访问4个不同含义的段。
1、CS+IP:用于代码段的访问,CS指向存放程序的段基地址,IP指向下条要执行的执行在CS端的偏移量,用这2个寄存器就可以得到一个内存物理地址,该地址存放着一条要执行的指令。
2、SS+SP:用于堆栈段的访问,SS指向堆栈段的基地址,SP指向栈顶了,可以通过SS和SP两个寄存器直接访问栈顶单元的内存物理地址。
3、DS+BX:用于数据段的访问。
DS中的值左移4位得到数据段起始地址,再加上BX中的偏移量,得到一个存储单元的物理地址。
4、ES+BX:用于附加段的访问。
ES中的值左移4位得到附加段起始地址,再加上BX中的偏移量,得到一个存储单元的物理地址。
三、32位CPU2种工作模式:实模式和保护模式1、实模式下,32位内存管理和16位CPU是一致的。
2、段基地址长达32位,每个段的最大容量4G,段寄存器的值是段地址的“选择器”,用该选择器从内存中得到一个32位的段地址,存储器单元的物理地址就是该段地址加上段内偏移量。
得到线性地址空间。
从管理和效率的角度出发,线性地址被分为固定长度的组,成为页,例如32位机器,线性地址最大可为4G,如果用4KB为一个页来划分,这样整个线性地址就被划分为2的20次方个页。
线性地址通过映射关系得到物理地址。
四、linux段管理所有段的基地址为0,每个段的逻辑地址空间范围为0~4G,因为每个段的基地址为0,因此,逻辑地址到线性地址映射不变,在Linux中所提到的逻辑地址和线性地址指的也就是同一地址。
看来,linux巧妙的把段机制给绕过去了,完全利用了分页机制。
linux2.6.29采用四级页管理架构。
1.1.2、物理地址的形成方式:段地址:将段寄存器中的数值左移4位补4个0(乘以16),得到实际的段地址。
段偏移:在段偏移寄存器中。
1)逻辑地址=段基地址+段内偏移量(段基地址寄存器+段偏移寄存器)2)由逻辑地址得到物理地址的公式为:(因为没有页式管理,所以这一步就得到了物理地址)物理地址PA=段寄存器的值×16+逻辑地址的偏移部分(注意!!)(段与段可能会重叠)按段式管理能访问2的32次方共4GB 的空间(2的16次方个段,每个段可以为2的16次方byte的大小),但是只有20位地址总线,因此只能访问1M的内存。
1.2、32位CPU:1)实模式:与16位时是一样的段寄存器的值×16就是段地址2)保护模式:段寄存器的值是一个选择器,间接指出一个32位的段地址段基地址长达32位,每个段的最大容量可达4G,段寄存器的值是段地址的“选择器”(segment selector),用该“选择器”从内存(segment descriptor)中得到一个32位的段地址,存储单元的线性地址就是段地址加上段内偏移量,这与32位CPU的物理地址的计算方式完全不同。
!!!线性地址=段基地址+段内偏移量/逻辑地址(段选择器指向的内存中存储的基地址+段偏移寄存器)!!! 这里不用乘以16或者32,因为段基址寄存器可以完全存下段地址。
32位CPU得到线性地址后,再经过页式管理就能得到物理地址二、页式管理2.1页:线性地址被分为固定长度的组,称为页。
例如32位的机器,每页4KB,可划为2的20次方个页(划分虚拟单元),32位机器的线性地址空间为4G。
2.2物理页:也称为页框、页桢;分页单元把所有的物理内存也划分为固定长度的管理单位(划分实在的单元),它的长度一般与线性地址页是相同的。
二者的区别:页划分的是线性地址,物理页划分的是实际的物理地址二者的联系:1、页的长度一般相同,2、通过mapping转换,存在映射关系。
通过分页管理模型,由线性地址得到物理地址1、分页单元中,页目录的地址存放在CPU的cr3寄存器中,是进行地址转换的开始点。
2、每个进程,都有其独立的虚拟地址空间,运行一个进程,首先要将它的页目录地址放到cr3寄存器中,将其他进程的页目录地址保存下来。
3、每个32位的线性地址被划分为三部分:页目录索引(10位);页表索引(10位);偏移(12位)(mapping映射关系由下图反映)上图中页的大小为2的12次方,即4K。
两级分页模型(页不算在内)******************************!!以上都是x86模式!!*********** *******************Linux内存管理Linux有限度地使用了Intel的段式管理机制,而完全采用了页式管理机制。
(也可以说没有采用段式管理)所有段的基地址全部为零(那么所有段都重合,相当于只有一个段)又因为"线性地址=段基地址+段内偏移量/逻辑地址偏移部分",所以"线性地址=逻辑地址"。
在linux系统中,逻辑地址=线性地址=虚拟地址物理地址<——>线性地址(mapping转换)linux页式管理linux2.6.29内核采用了四级页式管理架构,来兼容二级、三级管理架构的CPU。
页全局目录、页上级目录、页中间目录、页表(四级时,线性地址为64位)4-4-2进程地址空间(参考课件)linux操作系统采用虚拟内存管理技术,使得每个进程的地址空间都是独立的。
该空间大小是3G,用户看到的都是虚拟地址,无法看到实际的物理地址。
虚拟内存管理的优点:1、保护操作系统,2、用户程序可以使用比实际物理内存更大的地址空间。
linux将4G的虚拟地址空间划分为用户空间和内核空间。
用户空间:0~0xbfffffff(0~3G):用户进程通常情况下只能访问用户空间。
内核空间:0xc0000000~0xffffffff(3G-4G)用户空间对应进程,所以每当进程切换,用户空间就会跟着变化。
每个进程的地址空间都是独立的。
每个进程的页目录、页表不一样,因此通过页式转换就可以得到不同的物理地址。
例如:同一个用户程序运行多次,产生多个进程,每个进程访问的虚拟地址(逻辑地址、线性地址)都是一样的,但是每个进程都有一套独立的页目录、页表,因此得到的实际物理地址是不同的。
每个进程的页目录和页表就可以理解为进程独立的用户空间。
Fork(),execve(),malloc()等进程相关操作分配的内存,都是虚拟地址。
只有当进程去访问新获取的虚拟地址时,才会由“请页机制”去分配实际的物理地址。
实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际页框的程序。
该异常是虚拟内存机制赖以生存的基本保证——它会告诉内核去为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在地映射到了物理地址上。
动态分配——Linux内核中,动态分配内存使用函数kmalloc()头文件:#include<linux/slab.h>原型:void *kmalloc(size_t size,int flags) ——kfree,适用于分配小于128KB的内存功能:分配实际的物理页框,虽然返回的是虚拟地址,但已经有对应的物理页了。
参数:Flags取值为:1、GFP_KERNEL,分配实际的内存,分配不到时睡眠。
返回虚拟地址,但对应有实际的物理单元。
(16M-896M)2、GFP_ATOMIC,用来在进程上下文之外的代码(包括中断处理)中分配内存,从不睡眠。
3、__GFP_DMA,要求分配能够DMA的内存(物理地址在16M以下的页桢)4、__GFP_HIGHMEM,分配的内存位于高端内存。
(896M以上)当用完这些页,需要使用下列函数之一来释放它们:◎kfree函数按页分配◎get_zeroed_page(unsigned int flags)——free_page返回指向新页面的指针,并将页面清零。