
free_reserved_area()是内核启动后期用于将保留内存reserved memory释放回伙伴系统的核心函数。它通常服务于free_initmem()、free_initrd_mem()等场景把初始化代码段、initrd 等不再需要的物理内存归还给分配器。核心作用与设计意图它的设计目标很明确将一段物理地址范围内的“保留页”标记为可分配并归还给伙伴系统。这些页在启动早期被memblock标记为保留不参与常规分配当相关用途如内核初始化、initrd 挂载结束后通过这个函数将它们“解禁”。参数解析根据内核源码注释函数的语义如下/* * Free reserved pages within range [PAGE_ALIGN(start), end PAGE_MASK) * into the buddy system. The freed pages will be poisoned with pattern * poison if its within range [0, UCHAR_MAX]. * Return pages freed into the buddy system. */ unsigned long free_reserved_area(void *start, void *end, int poison, const char *s)参数含义start起始地址会被向上对齐到页边界end结束地址会被向下对齐到页边界poison毒化填充值在[0, UCHAR_MAX]范围内时用该值填充每页否则不毒化s描述字符串用于打印日志如unused kernel返回值释放到伙伴系统的页数。核心逻辑1. 地址对齐start (void *)PAGE_ALIGN((unsigned long)start); end (void *)((unsigned long)end PAGE_MASK);将start向上对齐、end向下对齐确保操作以页为单位。2. 遍历释放每一页for (pos start; pos end; pos PAGE_SIZE, pages) { struct page *page virt_to_page(pos); void *direct_map_addr; direct_map_addr page_address(page); if ((unsigned int)poison 0xFF) memset(direct_map_addr, poison, PAGE_SIZE); free_reserved_page(page); }这里有一个重要的历史修复早期代码直接对pos做memset()但如果pos是内核镜像的别名映射alias mapping且落在只读区域memset会失败。修复后改为先通过page_address(page)获取直接映射地址再对该地址毒化确保总是可写的。3. 打印日志if (pages s) pr_info(Freeing %s memory: %ldK\n, s, pages (PAGE_SHIFT - 10));释放成功后打印释放的内存大小KB。典型调用场景1. 释放内核初始化代码段// include/linux/mm.h static inline unsigned long free_initmem_default(int poison) { extern char __init_begin[], __init_end[]; return free_reserved_area(__init_begin, __init_end, poison, unused kernel); }内核启动完成后__init段标记为__init的函数和数据不再需要通过这个函数释放回伙伴系统。2. 释放 initrd 内存// arch/powerpc/mm/mem.c void __init free_initrd_mem(unsigned long start, unsigned long end) { free_reserved_area((void *)start, (void *)end, -1, initrd); }initrd 内容被解压/挂载后其占用的内存被释放。注意poison -1表示不毒化因为-1不在[0, 255]范围内。3. 毒化模式选择poison 0用 0 填充用于某些需要清零的场景。poison -1不毒化直接释放。具体毒化值如POISON_FREE_INITMEM由平台定义。历史演进2013 年Jiang Liu 的补丁增强了free_reserved_area()使其支持用 0 毒化内存并修复了 s390 上 initmem 毒化被误禁的问题。2018 年补丁修复了“非直接映射地址”调用失败的问题通过page_address()获取可写别名。2026 年重构提案将free_reserved_area()从page_alloc.c移至memblock.c并与memblock_free_late()合并共性逻辑。总结free_reserved_area()是保留内存的释放阀门它将[start, end)范围内按页对齐的保留页逐页毒化可选并通过free_reserved_page()归还伙伴系统返回释放的页数。它服务于free_initmem()释放内核初始化段、free_initrd_mem()释放 initrd等启动后期清理场景是内核把“启动专用内存”转化为“通用可分配内存”的关键一步。