分类: LINUX
2014-09-21 12:52:33
原文地址:编程感悟之Linux 作者:andyhzw
1,Linux 内存管理
在 linux 系统中,进程的 4GB 内存空间被分为两个部分------用户空间与内核空间。用户空间地址一般分布为 0 ~ 3GB,这样剩下的 3 ~ 4GB为内核空间,用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址。用户进程只有通过系统调用(代表用户进程在内核态执行)等方式才可以访问到内核空间。
每个进程的用户空间都是完全独立、互不相关的,用户进程各自有不同的页表。而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间地址有自己对应的页表,内核的虚拟空间独立于其他程序。
linux 中 1GB的内核地址空间又被划分为物理内存映射区、虚拟内存分配区、高端页面映射区、专用页面映射区和系统保留映射区这几个区域。
一般情况下,物理内存映射区最大长度为 896MB,系统的物理内存被顺序映射在内核空间的这个区域中。当系统物理内存大于 896MB时,超过物理内存映射区的那部分内存被称为高端内存(而为超过物理内存映射的内存通过被称为常规内存),内核在存取高端内存时必须将它们映射到高端页面映射区。
linux保留内核空间最顶部FIXADDR TOP ~ 4GB的区域作为保留区。
紧接着最顶端的保留区以下的一段区域为专用页面映射区(FIXADDR_START ~ FIXADDR TOP),它的总尺寸和每一页的用途有 fixed_address 枚举结构在编译时预定义,用 __fix_to_virt(index)可获取专用区内预定义页面的逻辑地址。其开始地址和结束地址宏定义如下;
#define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE)
#define FIXADDR_TOP ((unsigned long)__FIXADDR_TOP)
#define __FIXADDR_TOP 0xfffff000
接下来,如果系统配置了高端内存,则位于专用页面映射区之下的就是一段高端内存映射区,其起始地址为 PKMAP_BASE,定义如下:
#define PKMAP_BASE ((FIXADDR_BOOT_START - PAGE_SIZE*(LAST_PKMAP + 1)) & PMD_MASK)
其中涉及的宏定义如下:
#define FIXADDR_BOOT_START (FIXADDR_TOP - __FIXADDR_BOOT_SIZE)
#define LAST_PKMAP PTRS_PER_PTE
#define PTRS_PER_PTE 512
#define PMD_MASK (~(PMD_SIZE - 1))
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PMD_SHIFT 21
在物理区和高端映射区之间为虚拟内存分配区(VMALLOC_START ~ VMALLOC_END),用于 vmalloc() 函数,它的前部与物理内存映射区有一个隔离带,后部与高端映射区也有一个隔离带。
当系统物理内存超过 4GB 时,必须使用CPU 的扩张分页(PAE)模式所提供的64位页目录项才能取到 4GB以上的物理内存,这需要 CPU 的支持。加入了 PAE 功能的 Intel Pentium Pro 及其后的 CPU 允许内存最大可配置到 64 GB,具备 36 位物理地址空间寻址能力。
由此可见,在 3 ~ 4 GB 之间的内核空间中,从低地址到高地址依次为:物理内存映射区----- 隔离带----- vmalloc虚拟内存分配器----- 隔离带----- 高端内存映射区----- 专用页面映射区----- 保留区。
2,kmalloc()
void *kmalloc(size_t size,int flags);
给 kmalloc() 的第一个参数是要分配的块的大小,第二个参数为分配标志,用于控制kmalloc() 的行为。
最常用的分配标志是 GFP_KERNEL,其含义是在内核空间的进程中申请内存。kmalloc()的底层依赖 __get_free_pages()实现,分配标志的前缀 GFP 正好是这个底层函数的缩写。使用 GFP_KERNEL 标志申请内存时,若暂时不能满足,则进程会睡眠等待页,即会引起阻塞,因此不能再中断上下文或持有自旋锁的时候使用 GFP_KERNEL 申请内存。
3,slab
slab不是要代替 __get_free_pages(),其在最底层仍然依赖于 __get_free_pages(),slab在底层每次申请 1 页或多页,之后再分隔这些页为更小的单元进行管理,从而节省了内存,也提高了 slab 缓冲对象的访问效率。