get_free_pages_noprof()和get_zeroed_page_noprof()——返回虚拟地址的页面分配接口是对alloc_pages_noprof()的上层封装。它们与alloc_pages系列的区别在于返回值类型前者返回可直接使用的虚拟地址unsigned long后者返回struct page *。一、get_free_pages_noprof()unsigned long get_free_pages_noprof(gfp_t gfp_mask, unsigned int order) { struct page *page; page alloc_pages_noprof(gfp_mask ~__GFP_HIGHMEM, order); if (!page) return 0; return (unsigned long) page_address(page); } EXPORT_SYMBOL(get_free_pages_noprof);1. 清除__GFP_HIGHMEMgfp_mask ~__GFP_HIGHMEM这是关键操作。GFP_HIGHUSER等标志允许从ZONE_HIGHMEM分配但高端内存需要kmap()临时映射才能被内核访问而get_free_pages承诺返回可直接解引用的虚拟地址。因此必须清除__GFP_HIGHMEM确保页来自内核直接映射的 zoneZONE_NORMAL/ZONE_DMA。2. 委托alloc_pages_noprof()实际分配交给页面分配器主入口返回struct page *。3. 转换为虚拟地址return (unsigned long) page_address(page);page_address()返回页的内核虚拟地址。对于低端内存这是直接映射地址PAGE_OFFSET pfn * PAGE_SIZE对于高端内存只有在已 kmap 的情况下才有效——但__GFP_HIGHMEM已被清除所以这里始终安全。4. 返回 0 表示失败虚拟地址 0 是无效地址用作失败标志。二、get_zeroed_page_noprof()unsigned long get_zeroed_page_noprof(gfp_t gfp_mask) { return get_free_pages_noprof(gfp_mask | __GFP_ZERO, 0); } EXPORT_SYMBOL(get_zeroed_page_noprof);1. 强制__GFP_ZEROgfp_mask | __GFP_ZERO无论调用方是否请求都加上清零标志。分配器在post_alloc_hook()中会调用kernel_init_pages()清零。2. 固定 order 0只分配单页。这是get_zeroed_page的语义——一页清零内存。3. 复用get_free_pages_noprof()不重复实现直接复用。三、与相关函数的关系函数返回类型用途alloc_pages()struct page *需要页描述符的调用方__get_free_pages()unsigned long虚拟地址需要直接访问内存的调用方__get_free_page()unsigned long__get_free_pages(gfp, 0)的简写get_zeroed_page()unsigned long单页清零常用于安全敏感的分配get_free_pages_noprof()unsigned long上述函数去掉 profiling 的底层实现四、noprof后缀的含义现代内核中分配函数有noprof后缀的版本是不带 profiling 钩子的底层实现。上层宏如alloc_pages、get_free_pages会根据配置展开为带或不带 profiling 的版本。noprof版本直接暴露给不需要 profiling 的调用方避免不必要的开销。五、__GFP_HIGHMEM清除的深层原因这个细节很重要它体现了get_free_pages与alloc_pages的根本设计差异alloc_pages()可以返回高端内存的struct page *调用方若需要访问内容需自行kmap()。get_free_pages()承诺返回可直接解引用的虚拟地址因此必须限制在直接映射区。如果不清除__GFP_HIGHMEM在 32 位系统上可能返回高端内存页而page_address()对未映射的高端内存返回NULL或无效地址调用方解引用会崩溃。六、总结get_free_pages_noprof()和get_zeroed_page_noprof()是返回虚拟地址的分配接口前者通过清除__GFP_HIGHMEM确保页来自内核直接映射区然后调用page_address()转换为虚拟地址返回后者在前者基础上强制__GFP_ZERO并固定 order-0用于获取一页清零内存。它们的核心价值是让调用方无需关心struct page和映射细节直接拿到可用的虚拟地址。