说到内存一般会从两个角度来展开。其一是物理内存管理,其二是虚拟内存管理。
全景图:
从左至右依次是:
系统用一个全局变量struct page *mem_map来存放所有的物理页指针;
物理页的划分,ZONE_HIGHMEM/ZONE_NORMAL/ZONE_DMA;
虚拟地址空间划分;
物理内存管理有以下几个要点:
0. 伙伴系统是以页为单位来管理内存分配的,slab是以字节为单位来管理内存分配的,slab是在伙伴系统基础上对内存更小粒度的管理。
1. 分配物理内存最基本的函数是alloc_pages/__get_free_pages,两个最终会调用到alloc_pages_node。但__get_free_pages不能在高端内存分配页面;
2. GFP_KERNEL 分配内存很常用的掩码,可用在进程上下文,可能导致当前进程进入睡眠。
GFP_ATOMIC 原子分配,不会睡眠。
3. 分配内存是从最高级别开始往下分配的。比如指定__GFP_HIGHMEM,那么从HIGHMEM区看有没合适内存,没有那么看NORMAL区,再者DMA区;如果指定__GFP_NORMAL,那从NORMAL开始分配,如果NORMAL没有足够空间,那就从DMA。如果不指定gfp_mask,默认是__GFP_NORMAL。
4. kmalloc是基于slab分配器实现的。slab分配器只会分配ZONE_NORMAL/ZONE_DMA区域的内存。注意这两段区域是与虚拟地址空间0xc0000000~high_memory段一一映射的,这个映射是在系统启动过程完成的。所以,kmalloc函数分配的内存在物理上是连续的,分配的效率也较高,因为不用修改页表,重新建立映射。
struct kmem_cache用于管理其下的所有slab。
系统初始化期间,kmem_cache_init会遍历malloc_sizes数组,为每一个元素调用kmem_cache_create分配一个struct kmem_cache实例,并将其所在地址放在元素cs_cachep变量中。
以上数组每一个元素描述的大小就是slab分配器管理的小片内存大小。
虚拟内存管理有以下几个要点:
0. vmalloc分配内存步骤:
1. 虚拟内存的分配和释放是用红黑树来解决的,内核用struct vm_struct表示。
vm_struct有个flags,常用的有VM_ALLOC/VM_IOREMAP两个。VM_ALLOC表示映射的是物理内存,给vmalloc函数使用;VM_IOREMAP表示映射的是I/O地址空间,即设备内存,给ioremap函数使用。
2. 虚拟内存往往会分配比所要求的页再多一个页,这个多出来的页目的是对可能出现的越界访问做保护,实际并不会做物理内存的映射。如下图:
问题:
1. vmalloc分配内存,页表修改过程?