在用户空间动态申请内存
在用户空间中动态申请内存的函数为 malloc(),这个函数在各种操作上的使用都是一致的。与之对应的释放函数为 free()。
对于 Linux 而言,C 库的 malloc() 函数一般通过 brk() 和 mmap() 两个系统调用从内核申请内存。
在内核空间动态申请内存
在 Linux 内核空间中申请内存涉及的函数主要包括 kmalloc()、__get_free_pages() 和 vmalloc() 等。这里只介绍这3种。
1. kmalloc()
函数原型为:
void * kmalloc(size_t size, int flags);
size:要分配的块的大小
flags:分配标志,用于控制 kmalloc() 的行为,最常用是 GFP_KERNEL 和 GFP_ATOMIC
分配标志为 GFP_KERNEL 时,表示在内核空间的进程中申请内存。
使用 GFP_KERNEL 标志申请内存时,若暂时不能满足,则进程会睡眠等待也,即会引起阻塞,因此不能中断上下文或持有自旋锁的时候使用 GFP_KERNEL 申请内存。
由于在中断处理函数、tasklet 和内核定时器等非进程上下文中不能阻塞,所以此时驱动应当使用 GFP_ATOMIC 标志来申请内存。当使用 GFP_ATOMIC 标志申请内存时,若不存在空页,则不等待,直接返回。
2. __get_free_pages()
__get_free_pages() 系列函数/宏本质上是 Linux 内核最底层用于获取空闲内存的方法,因为底层的 buddy 算法以 2N 页为单位管理空闲内存,所以最底层的内存申请总是以 2N 页为单位的。
__get_free_pages() 系列函数/宏包括 get_zeroed_page()、__get_free_page() 和 __get_free_pages()。
kmalloc() 的底层依赖于 __get_free_pages() 来实现的。
3. vmalloc()
vmalloc() 一般只为存在于软件中(没有对应的硬件意义)的较大的顺序缓冲区分配内存,vmalloc() 远大于 __get_free_pages() 的开销,为了完成 vmalloc(),新的页表项需要被建立,因此,只是调用 vmalloc() 来分配少量的内存时不妥的。
vmalloc() 申请的内存使用 vfree() 释放。
vmalloc() 不能用在原子上下文中,因为它的内部实现了标志位 GFP_KERNEL 的 kmalloc()。
vmalloc() 在申请内存时,会进行内存的映射,改变页表项,不像 kmalloc() 实际用的是开机过程中就映射好的 DMA 和常规区域的页表项。因此 vmalloc() 的虚拟地址和物理地址不是一个简单的线性映射。
4. 总结
kmalloc() 和 __get_free_pages() 申请的内存在物理上连续,而 vmalloc() 申请的内存在物理上不连续。