Linux嵌入式 -- 内核 - 内存管理

1.  逻辑地址 线性地址 物理地址



段式管理: 16位CPU,20根地址总线,可寻址1M内存,但是只有16位的寄存器,64K。

逻辑地址  =  段基地址 + 段内偏移地址

物理地址 PA  = 段寄存的值 * 16  + 逻辑地址

段式管理: 32位CPU,两种模式  实模式 + 保护模式

实模式 和 16位CPU一样,段寄存器的值*16就是段地址

保护模式: 段基地址32位,每个段都有4G容量,段寄存器的值是一个选择器,间接指出一个32位的段地址。

页式管理: 线性地址被分为固定长度的组,称为页(page),

例如32位的机器,线性地址最大可为4G,如果用4KB为一个页来划分,这样整个线性地址就被划分为2的20次方个页。


1、分页单元中,页目录的地址放在CPU的cr3寄存器中,是进行地址转换的开始点。
2、每一个进程,都有其独立的虚拟地址空间,运行一个进程,首先需要将它的页目录地址放到cr3寄存器中,将其他进程的保存下来。
3、每一个32位的线性地址被划分为三部份:页目录索引(10位):页表索引(10位):

依据以下步骤进行地址转换:

1、装入进程的页目录地址(操作系统在调度进程时,把这个地址装入CR3)
2、根据线性地址前十位,在页目录中,找到对应的索引项,页目录中的项是一个页表的地址
3、根据线性地址的中间十位,在页表中找到页的起始地址
4、将页的起始地址与线性地址的最后12位相加,得到物理地址偏移(12位)。

Linux内核的设计并没有全部采用Intel所提供的段机制,仅仅是有限度地使用了分段机制

所有段的基地址均为0
由此可以得出,每个段的逻辑地址空间范围为0-4GB。因为每个段的基地址为0,因此,逻辑地址与线性地址保持一致(即逻辑地址的偏移量字段的值与线性地址的值总是相同的),在Linux中所提到的逻辑地址线性地址虚拟地址),可以认为是一致的。看来,Linux巧妙地把段机制给绕过去了,而完全利用了分页机制

前面介绍了i386的二级页管理架构,不过有些CPU(Alpha 64位)使用三级,甚至四级架构,Linux  2.6.29内核为每种CPU提供统一的界面,采用 了四级页管理架构,来兼容二级、三级、四级管理架构的CPU。




2. 虚拟内存

Linux操作系统采用虚拟内存管理技术使得每个进程都有独立的进程地址空间,该空间是大小为3G,用户看到和接触的都是虚拟地址,无法看到实际的物理地址。利用这种虚拟地址不但能起到保护操作系统的作用,而且更重要的是用户程序可使用比实际物理内存更大的地址空间

Linux将4G的虚拟地址空间划分为两个部分——用户空间与内核空间。用户空间从0到0xbfffffff,内核空间从3G到4G。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间。例外情况是用户进程通过系统调用访问内核空间

用户空间对应进程,所以每当进程切换,用户空间就会跟着变化。(虚拟地址相同------->页目录页表不同-------> 内存物理地址不同

创建进程fork()、程序载入execve()、动态内存分配malloc()等进程相关操作都需要分配内存给进程。这时进程申请和获得的不是物理地址,仅仅是虚拟地址。

实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际页框的程序。该异常是虚拟内存机制赖以存在的基本保证——它会告诉内核去为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在地映射到了物理地址上。


3. 内存分配

在Linux内核中,通常使用kmalloc来动态分配内存。
kmalloc 原型是:
 #include <linux/slab.h>
void *kmalloc(size_t size,int flags)
参数:size:要分配的内存大小。flags:分配标志, 它控制 kmalloc 的行为。

GFP_ATOMIC 用来在进程上下文之外的代码(包括中断处理)中分配内存,从不睡眠
GFP_KERNEL 进程上下文中的分配。可能睡眠。(16M-896M)
 __GFP_DMA 这个标志要求分配能够 DMA 的内存区(物理地址在16M以下的页帧 )
 __GFP_HIGHMEM 这个标志表示分配的内存位于高端内存。(896M以上)

如果模块需要分配大块的内存,那使用面向页的分配技术会更好
get_zeroed_page(unsigned int flags)
返回指向新页面的指针,并将页面清零。
__get_free_page(unsigned int flags)
和get_free_page类似,但不清零页面。
__get_free_pages(unsigned int flags,unsigned intorder)
分配若干个连续的页面,返回指向该内存区域的指针,但也不清零这段内存区域。

当程序用完这些页, 可以使用下列函数之一来释放它们:
void free_page(unsigned long addr)
void free_pages(unsignedlongaddr, unsigned  long order)
**如果释放的和先前分配数目不等的页面,会导致系统错误**


4. 内核空间

内核空间是由内核负责映射它并不会跟着进程改变,是固定的物理内存896MB以上的部分称之为高端内存


直接内存映射区(Direct Memory Region)从3G开始,最大896M的线性地址区间,我们称作直接内存映射区,这是因为该区域的线性地址和物理地址之间存在线性转换关系 线性地址=3G + 物理地址

动态内存映射区 (Virtual  malloc Region)该区域的地址由内核函数vmalloc来进行分配(上图第三条路),其特点是线性空间连续,但对应的物理空间不一定连续。vmalloc分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存。

永久内存映射区(PKMap Region )对于896MB以上的高端内存,可使用该区域来访问,访问方法:
1. 使用alloc_page(__GFP_HIGHMEM)分配高端内存页
2. 使用kmap函数将分配到的高端内存映射到该区域

固定映射区(Fixing MappingRegion)PKMap区上面,有4M的线性空间,被称作固定映射区,它和4G顶端只有4K的隔离带。固定映射区中每个地址项都服务于特定的用途,如ACPI_BASE等。





posted @ 2013-08-08 22:45  今晚打酱油_  阅读(283)  评论(0编辑  收藏  举报