内核和用户进程在分配内存时不同,内核更复杂,需更小心,不能太奢侈
物理页:内存管理基本单位,32位(4k),64位(8k),struct page代表每个物理页
struct page含引用计数(页是否空闲,若不空闲:1 进程页表映射 2页高速缓存(address_space)3 内核使用)
进程地址空间: 3G到4G为内核空间,内核空间分为
ZONE_DMA(虚拟地址3G开始的0-16MB),对应物理地址0-16MB,固定映射,供IO设备的DMA使用
ZONE_NORMAL(虚拟地址3G开始的16-896MB),对应物理地址16-896MB,固定映射
ZONE_HIGHEM(虚拟地址3G开始的896-1024MB),对应其余所有物理地址,动态映射(可能目前没有映射,可能映射了又取消又映射到另一块物理地址上了)
分区是相似特效的页进行分组,除上面3区外,还有ZONE_DMA32(其内页只能被32位设备访问)
不管映射关系是固定的还是动态的,使用页前必须分配内存(小于896MB内映射关系固定,但其中很多页并未有使用)
区的概念存在于内核空间也存在于物理内存中,分配内存一定是在物理内存中,只不过有些内存分配后(物理内存的0-896mb)是天然(固定)映射到内核空间,有些是(物理内存的896mb-可能4g)还没有做映射。
很明显,在ZONE_DMA和ZONE_NORMAL里分配内存一定是物理和虚拟地址都连续的。
页分配:
alloc_page: 分配一页,返回页page结构
alloc_pages:分配连续物理页,返回第一个页page结构
_get_free_page: 分配一页,返回页逻辑地址
_get_free_pages:分配连续物理页,返回第一个页逻辑地址
get_zeroed_page:分配一页,填充0,返回页逻辑地址
释放页:小心,只能释放自己分配的页
字节(内存块)分配:分配的内存可能占据多个页,不足一个页一般也会圆整。
kmalloc: 分配一个size大小物理连续内存块,返回指针(逻辑地址)。kfree释放
vmalloc:分配虚拟地址连续,物理不联系的内存,返回指针,在物理内存的高端内存里分配多个不连续物理页, 在内核空间的ZONE_HIGHEM里的vmalloc_start到 vmalloc_end分配一个vmalloc区映射到分配的物理页,(不需要分配器标志),必须建立页表项,用的少,一般获得大块内存时使用,vfree释放
malloc:用于用户空间内存分配,虚拟地址连续,物理不联系,和上面所有的都不一样
绝大多的页分配和字节(内存块)分配都需传递分配器标志,vmalloc没传
行为修饰符:_GFP_WAIT(分配时可睡眠)/_GFP_IO(可启动磁盘io)/_GFP_FS(可启动文件系统io) 等等
区修饰符:_GFP_DMA/_GFP_DMA32/_GFP_HIGHMEM(优先从highmem,其次normal) ,不指定优先从normal区分配
类型:_GFP_KERNEL(常用,可睡眠,行为修饰符里wait/io/fs 的结合)/_GFP_ATOMIC(不能睡眠,多用于中断处理程序,软中断,tasklet里内存分配,容易分配失败)
slab层:一个高速缓存对应某种结构(如task_struct), 高速缓存含多个slab,一个slab(满,部分满,空)含多个已分配内存的数据结构
栈上分配:用户栈大且可动态扩展,内核栈小而固定,使用内核栈进行局部变量分配(如系统调用)要省着点,别溢出了(thread_info都会被覆盖)
高端内存:
在物理内存的高于896MB分配空间后,并不一定已经映射了,不允许在高端内存分配了内存但没有映射可又要返回逻辑地址的情况
alloc_page(s)结合_GFP_HIGHMEM, 没有映射
_get_free_page(s)/get_zeroed_page/kmalloc不允许结合_GFP_HIGHMEM,因为返回不了逻辑地址(没有映射)
vmalloc 映射
kmap 永久映射给定物理页(传入struct page)到内核地址空间,用于可睡眠时,如果物理页在低端内存(本身已经是永久映射了),无效果,如果物理页在高端内存,建立映射,返回逻辑地址,kunmap 解除
kmap_atomic 临时映射 ,用于不能睡眠时,kunmap_atomic 解除
上图page_offset为3G处,物理内存映射为3G到3G+896mb, high_memory(3G+896mb到4G)又分为三个部分,分别用于vmalloc,kmap和kmap_atomic建立的映射。
每个cpu数据:每个cpu访问属于其自己数据,不需要锁,减少缓存失效