其中一些概念可以参考第一篇《地址、MMU、内存管理相关概念》
转载请注明原文地址:http://blog.csdn.net/ts_dchs/article/details/50241887
空间申请,地址转换关系:
(image source: http://blog.chinaunix.net/uid-20528014-id-315801.html )
注意:主要描述环境是Linux
1 内存申请释放
下面会提到的内存申请:
- malloc - free: 用户空间,逻辑空间连续 ( +relloc +valloc)
- kmalloc - kfree: 内核空间,物理连续,常规内存
- __get_free_pages - free_pages: 内核空间,物理连续,常规内存
- vmalloc - vfree: 内核空间,物理不连续,虚拟连续,会使TLB抖动,性能降低,高端内存。
- slab: kmem_cache_create - kmem_cache_destory
1 用户进程空间内存动态申请
malloc()
申请的空间在heap上,需要申请者用free()
释放。注意尽量成对出现,避免内存泄漏。注:C Linux的malloc常用brk()
和mmap()
系统调用实现。
++原型++:extern void *malloc(unsigned int num_bytes)
(stdlib.h in Linux)(malloc.h in windows)
通过void *
将返回的指针转化成任意类型,所以分配后常要进行强制类型转换在分配给相应类型的指针。
malloc()
返回指针可能是NULL
也就是失败,需要在开辟空间的时候加以判断。另外,malloc()
只负责映射分配不负责初始化,内容不一定是0.
++原型++:extern void *realloc(void *mem_address, unsigned int newsize)
realloc()
与malloc()
分配方式相同,不过会重新调整原来的空间。如果新空间比原来空间大,会先看直接扩展注册是否可行,不可行则分配新的逻辑地址,拷贝数据,释放原来空间。如果新空间更小可能会数据丢失。
在GNU系统中,malloc或realloc返回的内存块地址都是8的倍数(如果是64位系统,则为16的倍数)。如果你需要更大的粒度,要使用memalign或valloc。
++原型++:void * memalign(size_t boundary, size_t size)
分配以boundary倍数为边界的内存块。且boundary必须是2的幂。
++原型++:void * valloc(size_t size)
利用memalign来分配,不过boundary设定好是page大小。memalign(getpagesize(), size)
2 内核空间内存动态申请
kmalloc()
manual link
申请内存位于物理内存映射区,物理上也连续(对需要DMA的设备很重要)。和真实物理地址只有一个固定的offset。在设备驱动程序或者内核模块中动态开辟内存使用。返回的是线性地址。
++原型++:void *kmalloc(size_t size, int flags);
(linux/slab.h)
size是大小,flag是标志,GFP_KERNEL表示在内核空间进程中申请内存。其底层依赖__get_free_pages()
实现。使用这个flag后,如果不能满足,进程会睡眠等待页,可能会引起阻塞。因此不能在++中断上下文,spin lock++中使用GFP_KERNEL申请内存。
在++中断处理函数,tasklet,内核定时器++非进程上下文不能阻塞,应该用GFP_ATOMIC申请内存,不存在空闲会直接返回。
相应其他标志位定义于:include/linux/gfp.h。
kfree()
释放空间。
*kmalloc最大只能开辟128kB-16B(128k = 32*PAGESIZE),16个字节是被页描述符结构占用了。内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。默认最小32KB。
*CONFIG_LARGE_ALLOCS选项开启最大可分配32MB
*开辟的空间可以通过flag控制在DMA Zone或者HIGHMEM Zone中。
kmalloc()
用于物理连续内核态小内存分配,get_free_page
分配整页的地址。。
__get_free_pages()
Linux Kernel最底层使用的获取空间的方法。底层以page的2^n为单位管理空闲内存,所以内存页的申请是以page为单位。
get_zeroed_page(unsigned int flags);
指向一个已经清零的新page。
__get_free_page(unsigned int flags);
(是page不是pages)指向新页但不清零,实际上是用了order为0的一个函数。
++原型++:__get_free_pages(unsigned int flags, unsigned int order);
(linux/gfp.h)
获取多个pages数量是2^order,不清零。order最大是10或11,硬件相关。(4KB * 1024 =4MB Space)
/proc/buddyinfo 中有系统中每个内存区中的每个 order 有多少块可用,形如
Node 0, zone Normal 1027 339 ...
代表NUMA节点0(不是NUMA则只有0号),常规内存,2^0*PAGESIZE大小的块有1027个,2^1*PAGESIZE大小的块有339个以此类推。如果有11个数字,则order最大值为10,即能分配的最大空间为2^10*PAGESIZE(4KB)=4MB.
*前面三个函数的实现其实是调用了alloc_pages()
,该函数可以在用户空间,也可以在内核空间使用。返回struct page *
。
释放:
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
特别注意order前后要一致。
*kmalloc和get_free_page都在Kernel的常规内存做线性映射,所以得到的线性地址addr - PAG