PFN_UP/PFN_DOWN/PFN_PHYS/PHYS_PFN/PFN_ALIGN

📅 2026/8/27 6:33:51
PFN_UP/PFN_DOWN/PFN_PHYS/PHYS_PFN/PFN_ALIGN
PFN_UPPFN_UP是 Linux 内核中用于将一个物理地址向上取整转换为它所在的页帧号Page Frame Number的宏。工作原理它的定义如下#define PFN_UP(x) (((x) PAGE_SIZE - 1) PAGE_SHIFT)PAGE_SIZE是一个常量对于标准的 4KB 内存页其值为4096。PAGE_SHIFT是页面大小的位移位数对于 4KB 页其值为12。其计算逻辑是先将地址x加上(PAGE_SIZE - 1)相当于进行“向上取整”的修正然后再右移PAGE_SHIFT位即除以页面大小并丢弃余数从而得到x所在页的下一页的页帧号。对比PFN_DOWN与PFN_UP可以这样理解它们的区别PFN_DOWN(x)直接右移得到的是小于等于x的最大页帧号。例如PFN_DOWN(4097) 1。PFN_UP(x)先加(PAGE_SIZE - 1)再右移得到的是大于等于x的最小页帧号。例如PFN_UP(4097) 2。PFN_UP的使用示例在arch/x86/kernel/setup.c的parse_memmap_opt函数中就有PFN_UP的使用场景当解析到memmapnn[KMG]这类不带偏移量的参数时它会将指定的内存大小以字节为单位转换为页帧号用于限制内核可使用的最大内存范围。在该函数中它通过PFN_UP(mem_size)计算出需要保留的起始页帧号然后与max_possible_pfn进行比较最终确定max_pfn的值。这个操作是内核启动早期解析mem参数时的标准流程。PFN_DOWNPFN_DOWN是 Linux 内核中一个非常常用的宏它的核心作用是将一个物理地址或任意数值向下取整转换为它所在的页帧号Page Frame NumberPFN。工作原理它的定义非常简洁在include/linux/pfn.h文件中可以找到#define PFN_DOWN(x) ((x) PAGE_SHIFT)PAGE_SHIFT是一个常量代表了页面大小的位移数。对于标准的 4KB 内存页PAGE_SHIFT的值是12因为2^12 4096字节。因此PFN_DOWN(x)的本质就是对x进行右移PAGE_SHIFT位的操作。这个右移操作等价于将地址值除以页面大小并丢弃余数即向下取整从而得到该地址所在的内存页的编号。在split_mem_range()中的作用在split_mem_range()函数其实现就大量使用了PFN_DOWN宏。一个重要的补丁正是x86, mm: use PFN_DOWN in split_mem_range()它用这个宏替换了原有的手动移位操作 PAGE_SHIFT。这个替换带来了代码可读性的提升让页帧号的计算意图更清晰也统一了内核中的计算方式。在地址对齐计算中它常与round_up和round_down等宏配合使用例如PFN_DOWN(round_up(pos, PMD_SIZE))这样的写法在split_mem_range()源码中可见。PFN_PHYSPFN_PHYS是 Linux 内核中的一个宏用于将页帧号Page Frame Number, PFN转换为对应的物理地址Physical Address。它的核心定义非常简洁就是一个左移运算#define PFN_PHYS(x) ((phys_addr_t)(x) PAGE_SHIFT)其中PAGE_SHIFT是页面大小的位移数对于标准的 4KB 页面其值为 12。因此PFN_PHYS(x)的本质就是计算x * 4096这个物理地址值。为什么需要显式类型转换一个值得注意的细节是PFN_PHYS的定义中包含了(phys_addr_t)类型转换。这个转换是为了保证在 32 位系统上能正确处理超过 4GB 的物理地址。背景页帧号PFN的类型通常是unsigned long在 32 位系统上它只有 32 位。但是启用了物理地址扩展PAE的 32 位系统可以支持超过 4GB 的物理内存。问题如果直接进行(x) PAGE_SHIFT运算结果会被限制在 32 位内从而导致高地址被截断计算出的物理地址不完整。解决方案通过将x强制转换为phys_addr_t类型在支持 PAE 的系统上通常是 64 位再执行左移就能得到一个完整的、不会溢出的物理地址。这一修正使得PFN_PHYS能够被安全地用在lowmem_page_address()这类需要处理任何可能的物理页框号的函数中增强了代码的通用性和正确性。与 PFN 系列宏的对比可以将PFN_PHYS与同一系列的宏进行对比来更清晰地理解它的定位宏功能方向PFN_PHYS(x)页帧号 → 物理地址PFN 转 PHYSPHYS_PFN(x)物理地址 → 页帧号PHYS 转 PFNPFN_UP(x)物理地址向上取整得到下一页帧号向上取整PFN_DOWN(x)物理地址向下取整得到当前页帧号向下取整简单来说PFN_PHYS在页帧号和物理地址的转换中负责“正向”的一步是内核进行内存地址计算的常用工具。PHYS_PFNPHYS_PFN是 Linux 内核中与PFN_PHYS相对应的宏它的作用是将物理地址Physical Address转换为页帧号Page Frame Number, PFN。宏定义与工作原理它的核心定义同样非常简洁就是一个右移运算#define PHYS_PFN(x) ((unsigned long)((x) PAGE_SHIFT))其中PAGE_SHIFT是页面大小的位移数对于标准的 4KB 页面其值为 12。因此PHYS_PFN(x)的本质就是对物理地址x进行右移PAGE_SHIFT位即除以页面大小并向下取整得到该地址所在页的页帧号。为什么需要这个宏确保类型安全内核中物理地址的类型是phys_addr_t在某些架构或配置下它可能比unsigned long更宽例如支持大物理地址的 32 位系统。直接对phys_addr_t类型的变量进行位运算后赋值给unsigned long可能会产生编译器警告。PHYS_PFN宏通过显式的类型转换让代码意图更清晰也避免了潜在的警告。代码一致性与可读性在split_mem_range()这类函数中将物理地址转换为页帧号是常见的操作。使用PHYS_PFN宏统一这种转换可以使代码在风格上保持一致也更容易被理解。与PFN_PHYS的互补关系是PFN_PHYS执行的是反向操作。这两个宏共同构成了内核中物理地址与页帧号相互转换的标准接口是内存管理代码中非常基础的工具。宏功能方向PFN_PHYS(x)页帧号 → 物理地址PFN 转 PHYSPHYS_PFN(x)物理地址 → 页帧号PHYS 转 PFN在任何需要处理物理地址与页帧号转换的代码中都应优先使用这两个宏而不是手动进行位移运算。它们不仅能使代码意图更明确也能确保在不同架构下的正确性。PFN_ALIGNPFN_ALIGN是 Linux 内核中用于将物理地址向上对齐到页面大小的边界的宏。它的核心功能是确保一个给定的地址值始终是内存页大小的整数倍。工作原理它的定义如下#define PFN_ALIGN(x) (((unsigned long)(x) (PAGE_SIZE - 1)) PAGE_MASK)PAGE_SIZE是页面大小对于标准的 4KB 内存页其值为4096。PAGE_MASK是一个掩码其值为~(PAGE_SIZE - 1)。用于清除地址的低位。其计算逻辑是先将地址x加上(PAGE_SIZE - 1)然后与PAGE_MASK进行按位与操作。这相当于对地址进行向上取整得到下一个页对齐的地址。与round_up/PFN_UP的对比内核中提供了多种对齐方式可以这样理解它们的区别PFN_ALIGN(x)直接对物理地址进行页对齐返回一个对齐的物理地址值。round_up(x, PAGE_SIZE)功能相同但round_up是更通用的宏。PFN_UP(x) PAGE_SHIFT分两步完成相同操作先转为页帧号再转回地址。内核社区正在逐步统一这些不同的实现方式例如有补丁曾将PFN_ALIGN替换为ALIGN以提高代码一致性和可读性。在split_mem_range()等启动函数中对地址进行对齐的核心计算就是基于与PFN_ALIGN类似的原理。