关于linux内存的管理

来源:互联网 发布:mac airplay怎么用 编辑:程序博客网 时间:2024/04/30 00:25

  (1)  最近又稍微看了linux内存的管理,又稍微有一点收获,来这里记录一下,以防以后又忘记了。(都是针对32位的线性地址)

    首先是linux内核中从逻辑地址到物理地址的转化。由于X86要支持段式映射,l段式映射是将段描述符中的基地址加上偏移量的寻址方式。ldtr或者gdtr寄存器指向段描述符表的首地址,16位段选择寄存器中的前13位用于选择是段描述符表中的对应项(也就是段描述符),后三位包括权限检查、选择ldtr或者gdtr寄存器等。段描述中需要关注的是base字段,是段映射中的基地址。linux中为了兼容虽然也用了段映射,但是base都是0x0000000,所以在linux中逻辑地址等于线性地址。段映射就讲这么多了,不是重点,页映射才是重点。

    上面讲到由逻辑地址转化为了线性地址(linux其实是相等的)。线性地址通过页映射转换为物理地址。页映射把32位的线性地址划分为3段,前10位页目录项,中间10位页面项,后12位是页内偏移。寄存器cr3中存放当前使用的页表项基地址,指向进程的页表项。每个进程都有一个页表项,当发生进程切换的时候要更改cr3中的值以使用自己的进程空间。寻址过程是:

1.通过cr3找到页目录表的基地址,然后通过线性地址的前10位找到相应偏移的页目录项,该目录项中存放相应页表的基地址。

2.找到页表基地址后,通过中间10位找到相应偏移的页表项。

3.通过偏移量找到相应偏移的字节。


(2)linux中4G的线性地址空间,内核占1G(0xC0000000以上),用户空间站低3G。值得一提的是在1G的内核空间中分为3个区:DMA,NORMAL,HIGHMEMORY三个区。DMA一般低16M,NORMAL(16~896M),高128M是HIGHMEMORY。其中DMA区和NORMAL区在linux启动完成后一般是固定映射了物理内存的低896M页框,这也就是为什么在内核中有物理地址和线性地址间的转换只要加上或者减去0xC0000000。这里的内存可以通过kmalloc或者 get_free_pages获取,这两个函数获取的是直接是线性地址。alloc_pages获取的是页框地址,要获得线性地址还要进行相应的转化。page_address可以进行转化,page_address先判断是否是高端内存,如果不是直接加上0xc0000000就是线性地址。如果是则比较复杂,先检查是否已经映射了相应的线性地址有就直接返回地址,没有就要分配页目录和页面进行映射,然后返回线性地址。

    高端内存是没有映射的,使用的时候要临时进行映射到线性地址。

1、永久内核映射:直到取消映射才不能用了。kmap进行永久映射,这种映射可能会睡眠。

2、临时内核映射:kmap_atomic进行临时映射,进程不会睡眠;

3、非连续映射:vmalloc可以进行非连续映射,分配的内存在线性地址连续,物理地址可能不连续。

永久内核映射和临时内核映射,都由内核指定了需要进行映射的页面,也就是说指定了页描述符(页描述符和物理页框之间的关系是固定不可变的),在永久内核映射中,内核只需要在永久内核映射区找到空闲的,也就是未被映射的线性地址对应的页表项,然后将其分配给page即可,若找不到则将阻塞申请建立映射的进程;而临时内核映射更直接,连进行映射的线性地址窗口都是固定的,若是其已经分配给了某个页框,则直接抢过来用,因此之前的映射就被覆盖了(那么怎么保证数据安全呢?看到大概有两种保证:1.这个区域是percpu的,也就是每个cpu都有一个。2.在进行临时映射的时候要禁止中断,防止抢占。),体现出了临时性。非连续内存分配,内核不用指定具体的page,只需指定要申请的内存大小,内核将在非连续内存分配区找到一块相应大小虚拟地址空间,然后再由伙伴系统分配页框,还要通过slab分配器为一些数据结构分配内存,最后再用同样的方式(设置PTE表项)来建立映射。

详细解释见:http://blog.csdn.net/vanbreaker/article/details/7591844

(3)进程地址空间

    进程的地址空间在创建进程时分配,开始分配的时候是没有映射到物理地址的。只有在使用的时候发现对应线性地址的页表不存在则产生一个缺页异常,内核捕获到这个异常后开始映射,分配页目录和页面项,分配物理页框。编程引发的内存错误不一样,内存错误可能是由于不再进程地址空间,或者权限不够的时候产生,内核发送SIGSEGV信号,产生段错误。

  linux进程地址空间由进程描述符中的struct  mm_struct指向。mm中包括各种线性区的地址和长度。每个进程空间可能包含很多个不连续线性区,mm中分别用链表和红黑树组织线性区,一般遍

历时用链表,查找时用红黑树。每个进程空间最多可以有65536个线性区,在/proc/中可以更改这个数字。每个线性区由struct  vm_area_struct表示,包括线性区的起始地址。

下面是一张线性地址空间组织图。

0 0