@@ -4,7 +4,7 @@ categories:
44 - Linux 学习
55abbrlink: 484892ff
66date: 2024-10-24 15:00:00
7- updated: 2024-11-27 16:40 :00
7+ updated: 2024-11-27 18:15 :00
88---
99
1010<meta name="referrer" content="no-referrer"/>
@@ -4254,7 +4254,7 @@ int main(int argc, char *argv[])
42544254void *kmalloc(size_t size, gfp_t flags)
42554255```
42564256
4257- **最常用的分配标志是 GFP_KERNEL,含义是在内核空间的进程中申请内存。** kmalloc() 的底层是依赖 `__get_free_pages()` 实现 。使用 GFP_KERNEL 标志申请内存时,若暂时不能满足,进程会睡眠等待,即会引起阻塞,因此**不能在中断上下文或持有自旋锁的时候使用 GFP_KERNEL 申请内存**。
4257+ **最常用的分配标志是 GFP_KERNEL,含义是在内核空间的进程中申请内存。kmalloc() 的底层是依赖 `__get_free_pages()` 实现的 。使用 GFP_KERNEL 标志申请内存时,若暂时不能满足,进程会睡眠等待,即会引起阻塞,因此不能在中断上下文或持有自旋锁的时候使用 GFP_KERNEL 申请内存。**
42584258
42594259**在中断处理函数、tasklet 和内核定时器等非进程上下文中不能阻塞,应当使用 GFP_ATOMIC 标志申请内存。**当使用 GFP_ATOMIC 标志申请内存时,若不存在空闲页,则不等待,直接返回,避免了睡眠阻塞的问题。
42604260
@@ -4275,3 +4275,54 @@ void *kmalloc(size_t size, gfp_t flags)
42754275
42764276kmalloc() 申请的内存应使用 kfree() 函数释放,类似用户空间的标准 C 库函数 malloc() 和 free() 的关系。
42774277
4278+ #### __get_free_pages() 系列函数
4279+
4280+ **`__get_free_pages()` 系列函数/宏本质上是 Linux 内核最底层用于获取空闲内存的方法。底层的 buddy 算法以 2n 页为单位管理空闲内存,故最底层的内存申请总是以 2n 页为单位的。**
4281+
4282+ `__get_free_pages()` 系列函数/宏包括 get_zeroed_page()、`__get_free_page()` 和 `__get_free_pages()`。
4283+
4284+ ```c
4285+ // 该函数返回一个指向新页的指针并且将该页清零。
4286+ // 注意这里的指针使用 unsigned long 类型表示,类似 LarkSDK 中 LLog 对指针的处理。
4287+ unsigned long get_zeroed_page(gfp_t gfp_mask);
4288+
4289+ // 该宏返回一个指向新页的指针但是该页不清零。
4290+ #define __get_free_page(gfp_mask) \
4291+ __get_free_pages((gfp_mask), 0)
4292+
4293+ // 该函数可分配多个页并返回分配内存的首地址,分配的页数为 2 的 order 次方,分配的页不清零。order 允许的最大值是 10(1024 页)或者11(2048 页),这取决于具体的硬件平台。
4294+ unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
4295+ ```
4296+
4297+ `__get_free_pages()` 和 get_zeroed_page() 在实现中调用了 alloc_pages() 函数。**alloc_pages() 既可以在内核空间分配,也可以在用户空间分配。**
4298+
4299+ ```c
4300+ // 参数含义与 __get_free_pages() 类似,但它返回分配的第一个页的描述符而非首地址(前面的 unsigned long)。
4301+ // 这里的描述符使用 struct page * 指针描述的。
4302+ struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);
4303+ ```
4304+
4305+ 该系列函数使用如下接口释放:
4306+
4307+ ```c
4308+ void free_pages(unsigned long addr, unsigned int order);
4309+ #define free_page(addr) free_pages((addr), 0)
4310+ ```
4311+
4312+ `__get_free_pages()` 系列函数在使用时,申请标志的值与 kmalloc() 完全一致,各标志的含义也与完全一致。显而易见,因为 kmalloc() 底层是基于 `__get_free_pages()` 实现的。
4313+
4314+ #### vmalloc()
4315+
4316+ **vmalloc() 一般只为存在于软件中(没有对应的硬件意义)的较大的顺序缓冲区分配内存,vmalloc() 远大于 `__get_free_pages()` 的开销。**为了完成 vmalloc(),新的页表项需要被建立。因此,调用 vmalloc() 来分配少量的内存(如 1 页以内的内存)是不妥的。
4317+
4318+ 类似的,分配使用 vmalloc(),释放使用 vfree()。
4319+
4320+ ```c
4321+ void *vmalloc(unsigned long size);
4322+ void vfree(const void *addr);
4323+ ```
4324+
4325+ **vmalloc() 不能用在原子上下文中,因为内部实现使用了标志为 GFP_KERNEL 的 kmalloc()。**
4326+
4327+ **vmalloc() 在申请内存时,会进行内存的映射,改变页表项,不像 kmalloc() 实际用的是开机过程中就映射好的 DMA 和常规区域的页表项。**故 vmalloc() 的虚拟地址和物理地址不是一个简单的线性映射。
4328+
0 commit comments