天天看点

内存管理相关知识

        对于32位的系统,它的寻址空间为4GiB,linux系统通常会把这个4GiB的虚拟地址空间按3:1进行分配,即前面3GiB用于用户空间,后而1GiB用于内核空间。

        用户空间通过中断异常或系统调用切换到内核空间,用户空间访问内存时,它不会直接去访问物理内存,而是通过页表机制去访问,虚拟地址与物理地址之间的关系如图1所示:

内存管理相关知识

图1 32位处理上虚拟地址与物理地址之间关系

        如图1所示,物理内存被映射到内核空间,如用户空间要对物理内存进行访问,它是通过页表来进行访问的,用户空间用的是虚拟地址。根据实现物理内存大小,将物理内存映射到内核空间后,还会对内核地址空间进行划分,32位系统上内存地址空间划分如图2所示:

内存管理相关知识

图2 32位系统上内核空间划分

        根据3:1划分虚拟内存空间后,内核空间的地始地址__PAGE_OFFSET从0xc000000开始,将物理内存映射到__PAGE_OFFSET开始的地方,这块区域是内存直接映射区域,这部分对应的虚拟地址x,它和物理地址之间存在 一个简单的线性平移,这部分的虚拟地址和物理地址可以通过两个宏进行转换:

#define __pa(x) ((unsigned long (x) - PAGE_OFFSET)   返回虚拟地址对应的物理地址;

#define __va(x) ((void *) (unsigned long)(x) + PAGE_OFFSET)   返回物理地址对应的虚拟地址;

        在32系统中以3:1分配比例分配内核和用户空间的,由于内核空间的虚拟地址最大只有1GiB,所以最多只能映射1GiB,而且由于在内核空间里需要留出一些虚拟地址空间来做其它目的,可以直接映射的物理内存只有896MB,从__PAGE_OFFSET到high_memory最大只有896MB,剩下high_memory开始的虚拟内存空间用于管理vmalloc内存管理,持久 映射和固定映射。如剩下的虚拟内存空间为128MiB,通常在high_memory开始外会有8MiB的内存安全隙,防止不正常的内存不访问。

         在内核中也有与C标准库中用于申请和释放内存的函数存在,它们分别是:

kmalloc(size,flags),size为要申请的内存大小,flags为申请内存时指定的标志,用于指定用于分配内存的内存域(DMA,Normal, highmem),或用于指定内存分配器的行为,如__GFP_WAIT表示分配内存时可以等待和调度,__GFP_HIGH表示内存分配时不能被中断,用于原子分配。

kfree(ptr),ptr是指向由kmalloc申请的内存。

         kmalloc是基于slab分配器的一种内存管理方式,也就是说得先用kmem_cache_create来创建一个内存缓存,然后再通过kmem_cache_alloc和kmem_cache_free来申请内存。

         通过kmalloc申请内存时,有可能得到多于你申请内存大小,由它申请的内存长度都是2的幂次,范围2的5次32字节(内存页为4096)或64字节到2的25次32M字节,上限可以通过KMALLOC_MAX_SIZE设置,默认上限为128K字节,上限它是根据系统页大小和最大允许的分配阶计算:

#define  KALLOC_SHIFT_HIGH    ((MAX_ORKER + PAGE_SHIFT -1) <= 25 ? (MAX_ORDER + PAGE_SHIFT -1) : 25) 

#define KMALLOC_MAX_SIZE     (1UL << KMALLOC_SHIFT_HIGH)

          在调用 kmalloc申请内存时,内存会根据分配内存size找到slab分配器中合适的大小,然后再根据这个大小,从缓存中分配内存;如果不能找到合适的大小,最会分配稍大小size的内存,但不会分配小于size的内存。

          通过上面的kmalloc分配内存时,得到的连续的物理内存,当系统运行很长间后,由于内存碎片等原因,很难再去分配 一个很大的连续物理内存,内核可以和用户空间一样通过申请虚拟内存空间,然后通过分页机制去访问实际内存来解决这个问题。在内核中,有专门用于申请物理内存不连续,但虚拟内存连续的内存空间:

void * vmalloc(unsigned long size);

         对于vmalloc它只要指定分配的虚拟内存大小就行。由vmalloc分配得到的是连续的虚拟内存,但物理内存不一定是连续的,由于低端内存域的内存相对比较宝贵,不应该浪费在vmalloc分配中,所以在由vmalloc分配内存时,通常是从zone_highmem内存域分配,也就是说从highmem分配虚拟内存的优先级高于normal和dma内存域。

         由vmalloc分配内存时分为3个步骤:首先找到一个合适的内存域;然后从这个内存域中分配物理内存;最后将这些物理内存连续的映射到vmalloc区域。