🔥博客主页:PannLZ
😘欢迎关注:👍点赞🙌收藏✍️留言
文章目录
- 内核空间动态内存申请
- 1.kmalloc()
- 2._ _get_free_pages()
- 3.vmalloc()
内核空间动态内存申请
1.kmalloc()
#include <linux/slab.h>
void *kmalloc(size_t size, int flags);
参数:
- 分配的块的大小
- 分配标志,用于控制kmalloc的行为。
最常用的分配标志是
GFP_KERNEL
,其含义是在内核空间的进程中申请内存。kmalloc()
的底层依赖于__get_free_pages()
来实现,分配标志的前缀GFP正 好是这个底层函数的缩写。使用GFP_KERNEL
标志申请 内存时,若暂时不能满足,则进程会睡眠等待页,即 会引起阻塞,因此不能在中断上下文或持有自旋锁的 时候使用GFP_KERNE
申请内存。
其他一些申请标志:
GFP_USER | 用来为用户空 间页分配内存,可能阻塞 |
---|---|
GFP_HIGHUSER | 类似 GFP_USER,但是它从高端内存分配 |
GFP_DMA | 从 DMA区域分配内存 |
GFP_NOIO | 不允许任何I/O初始 化 |
GFP_NOFS | 不允许进行任何文件系统调用 |
__GFP_HIGHMEM | 指示分配的内存可以位于高端内 存 |
__GFP_COLD | 请求一个较长时间不访问的 页 |
__GFP_NOWARN | 当一个分配无法满足时,阻止 内核发出警告 |
__GFP_HIGH | 高优先级请求,允许 获得被内核保留给紧急状况使用的最后的内存页 |
__GFP_REPEAT | 分配失败,则尽力重复尝试 |
__GFP_NOFAIL | 标志只许申请成功,不推荐 |
__GFP_NORETRY | 若申请不到,则立即放弃 |
kzalloc()
kzalloc()
函数在本质上就是 kmalloc()
函数,只不过多了一个自动帮我们把所申请到的内存清零的操作而已。
kcalloc()
kcalloc()
函数的本质也是 kmalloc()
函数。它相较于 kzalloc()
函数又多了一层“数量”的封装。
/**
* kcalloc - allocate memory for an array. The memory is set to zero.
* @n: number of elements.
* @size: element size.
* @flags: the type of memory to allocate (see kmalloc).
*/
static inline void *kcalloc(size_t n, size_t size, gfp_t flags)
{
return kmalloc_array(n, size, flags | __GFP_ZERO);
}
static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
{
if (size != 0 && n > SIZE_MAX / size)
return NULL;
return __kmalloc(n * size, flags);
}
使用上面三个函数申请的内存都应使用kfree()
释 放,这个函数的用法和用户空间的free()
类似
2._ _get_free_pages()
_ _get_free_pages()
系列函数/宏是 kmalloc()实现的基础,_ _get_free_pages()系列函数/宏包括 get_zeroed_page()
、__get_free_page()
和_ _get_free_pages()
。
get_zeroed_page(unsigned int flags); //该函数返回一个指向新页的指针并且将该页清零。
__get_free_page(unsigned int flags);
//该宏返回一个指向新页的指针但是该页不清零,它实际上为:
#define __get_free_page(gfp_mask) \
__get_free_pages((gfp_mask),0)
//就是调用了下面的__get_free_pages()申请1页。
__get_free_pages(unsigned int flags, unsigned int order);
/*该函数可分配多个页并返回分配内存的首地址,分配的页数为 2^order,分配的页也不清零。order 允许的最大值是 10(即 1024 页)或者 11(即2048 页),依赖于具体的硬件平台。*/
__get_free_pages()和get_zeroed_page()在 实现中调用了alloc_pages()
函数, alloc_pages()
既可以在内核空间分配,也可以在用 户空间分配,其原型为:
struct page * alloc_pages(int gfp_mask, unsigned long order);
/*参数含义与__get_free_pages()类似,但它返
回分配的第一个页的描述符而非首地址。*/
使用_ _get_free_pages()
系列函数/宏申请的内存应使用下列函数释放:
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
__get_free_page
s等函数在使用时,其申请标志 的值与kmalloc()
完全一样,各标志的含义也与kmalloc()
完全一致,最常用的是GFP_KERNEL
和GFP_ATOMIC
。
3.vmalloc()
vmalloc()
一般用在为只存在于软件中(没有对应的硬件意义,因为分配的内存区域在物理内存中可能是分散的,而不是一个连续的物理内存块)的较大的顺序缓冲区分配内存,开销远大于__get_free_pages()
,为了完成 vmalloc(),新的页表需要被建立。因此,只是调用 vmalloc()来分配少量的内存(如 1 页)是不妥的。
vmalloc()
申请的内存应使用 vfree()
释放,vmalloc()和 vfree()的函数原型如下:
void *vmalloc(unsigned long size);
void vfree(void * addr);
vmalloc()不能用在原子上下文中,因为它的内 部实现使用了标志为
GFP_KERNEL
的kmalloc()
。使用vmalloc()函数的一个例子函数是
create_module()
系统调用,它利用vmalloc()函 数来获取被创建模块需要的内存空间。vmalloc()在申请内存时,会进行内存的映射, 改变页表项,不像kmalloc()实际用的是开机过程中 就映射好的DMA和常规区域的页表项。因此 vmalloc()的虚拟地址和物理地址不是一个简单的线 性映射。