内核把物理页作为内存管理的基本单位。处理器的最小可寻址单位为字节。然而从虚拟内存的角度看,页就是最小单位。
内核用struct page结构表示系统中的吗每个物理页,位于
struct page{
unsigned long flags;//页的装填
atomic_t _count;//页的引用计数
atomic_t _mapcount;
unsigned long private;
struct address_space *mapping;
pgoff_t index;
struct list_head lru;
void *virtual;
};
12.2区
由于硬件的限制,内核将页划为不同的区。
Linux主要存在四种区:
- ZONE_DMA——这个区包含的页能用来执行DMA操作。
- ZONE_DMA32——和ZONE_DMA类似,该区包含的页面可用来执行DMA操作;区别是该区的页面只能被32位设备访问。
- ZONE_NORMAL——能正常映射的页
- ZONE_HIGHEM——“高端内存”,其中的页不能永久的映射到内核地址空间
Linux把系统的也划分为区,形成不同的内存池。值得注意:不能同时从两个区分配内存,即分配不能跨越区域限制。
12.3 kmalloc()kmalloc()函数和malloc()类似,多一个flags参数。同malloc,kmalloc不对分配的内存空间清零。
void *kmalloc(size_t size,gfp_t flags),位于头文件
该函数返回一个指向内存块的指针,其内存块至少要有size大小。所分配的页在物理上是连续的。
内核最常用的类型标志是GFP_KERNEL,这种分配可能引起睡眠,因此不能用于中断上下文中。
与其截然相反的标志是GFP_ATOMIC,这个标志表示不能睡眠的内存分配,因此如果没有足够的连续内存块可用,内核也很可能无法释放出可用内存,因为内核不让调用者睡眠。即在当前代码不能睡眠(处于中断上下文),只能选择GFP_ATOMIC。
与之对应的是kfree()。void kfree(const void *ptr);
12.4 vmalloc()vmalloc()函数的工作方式类似于kmalloc(),但是vmalloc()分配的内存虚拟地址是连续的,物理地址无需连续。同时这也是用户空间分配函数的工作方式:由malloc()返回的页在进程的虚拟地址空间是连续的,但是不保证他们在物理RAM中也连续。
尽管vmalloc()可以将不连续的物理地址映射为连续的虚拟地址,但是很多内核大妈仍然使用kmalloc(),因为就性能而言,vmalloc()函数把物理上不连续的页转换为虚拟地址空间上连续的页,需要建立页表项。同时,通过vmalloc()获得的页必须挨个进行映射,这会导致比内存映射大的多的TLB抖动。
12.5 slab层slab分配器的几个原则:
- 频繁使用的数据结构也会频繁的分配和释放,因此应当缓存他们
- 频繁分配和挥手会导致内存碎片。为避免这种现象,空闲链表的缓存会连续的释放。因为已释放的数据结构又会重新放回空闲链表,因此不会导致碎片。
- 回收的对象可以立即投入下一次分配,对于频繁的分配和释放,空闲链表能够提高其性能。
- 对存放的对象进行着色,防止多个对象映射到相同的高速缓存行。
- 如果让部分缓存专属于单个处理器,那么,分配和释放就可以在不加SMP锁的情况下进行。
- 如果分配器是NUMA相关的,他就可以从相同的内存节点为请求者进行分配
kmalloc()建立在slab层之上,使用了一组通用高速缓存。然后这些高速缓存又被划分为slab,每个slab由一个或多个物理上连续的页组成。
slab层的特性:只有当给定的高速缓存部分中既没有满没有空的slav时才会调用页分配函数。且只有当可用内存变得紧缺时,系统试图释放出更多的内存以供使用;或高速缓存显示地被撤销时,才会调用释放函数。



