页面分配之快速路径

303 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

前面提到伙伴系统是Linux内核中基本的内存分配系统。伙伴系统的概念不难理解,但是一直以来,分配物理页面是内存管理中最复杂的部分,它涉及页面回收、内存规整、直接回收内存等相当错综复杂的机制。本节关注在内存充足的情况下如何分配连续物理内存。读者阅读完本书中的内存管理相关内容后,可以思考在最糟糕情况下页面分配器是如何分配连续物理页面的。

分配物理页面的接口函数

内核中分配物理内存页面的常用接口函数是alloc_pages(),它用于分配一个或者多个连续的物理页面,分配的页面个数只能是2的整数次幂。相比于多次分配离散的物理页面,分配连续的物理页面有利于缓解系统内存的碎片化问题,内存碎片化是一个很让人头疼的问题。 1.分配物理页面的核心接口函数 alloc_pages()函数的参数有两个,gfp_mask表示分配掩码,order表示分配级数。

<include/linux/gfp.h>


struct page * alloc_pages(gfp_t gfp_mask, unsigned int order)

alloc_pages()函数用来分配2的order次幂个连续的物理页面,返回值是第一个物理页面的page数据结构。第一个参数是gfp_mask;第二个参数是order,请求的order需要小于MAX_ORDER,MAX_ORDER通常默认是11。 另一个很常见的接口函数是__get_free_pages(),其定义如下。


<mm/page_alloc.c>
 
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

__get_free_pages()函数返回的是所分配内存的内核空间虚拟地址。如果所分配内存是线性映射的物理内存,则直接返回线性映射区域的内核空间虚拟地址;__get_free_pages()函数不会使用高端内存,如果一定需要使用高端内存,最佳的办法是使用alloc_pages()函数以及kmap()函数。注意,在64位处理器的Linux内核中没有高端内存这个概念,它只实现在32位处理器的Linux内核中。

分配一个物理页面

如果需要分配一个物理页面,可以使用如下两个封装好的接口函数,它们最后仍调用alloc_pages(),只是order的值为0。

#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)

#define __get_free_page(gfp_mask) \
        __get_free_pages((gfp_mask), 0)

如果需要返回一个全填充为0的页面,可以使用如下接口函数。

unsigned long get_zeroed_page(gfp_t gfp_mask)

使用alloc_page()分配的物理页面理论上可能被随机地填充了某些垃圾信息,因此在有些敏感的场合下需要先把分配的内存清零再使用,这样可以减少不必要的麻烦。

页面释放函数

void __free_pages(struct page *page, unsigned int order);
#define __free_page(page) __free_pages((page), 0)
#define free_page(addr) free_pages((addr), 0)

释放时需要特别注意参数,传递错误的page指针或者错误的order值会引起系统崩溃。__free_pages()函数的第一个参数是待释放页面的page指针,第二个参数是order。__free_page()函数用于释放单个页面。