PFN_DOWN是 Linux 内核中用于将物理地址转换为页帧号(PFN,Page Frame Number)的宏。它定义在include/linux/pfn.h中,是内存管理中地址转换的基础工具。
定义与原理
#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
它的核心逻辑是将地址右移PAGE_SHIFT位,相当于除以PAGE_SIZE(通常是 4096 字节)。
在 x86 架构下,PAGE_SHIFT为 12,因此:
PFN_DOWN(addr) = addr / 4096
核心作用
将物理地址转换为它所在的页帧号。页帧号是物理内存管理的基本单位,内核用 PFN 来索引struct page数组(即vmemmap)。
| 表达式 | 结果 | 说明 |
|---|---|---|
PFN_DOWN(0) | 0 | 地址 0 属于第 0 页 |
PFN_DOWN(4095) | 0 | 仍在第 0 页内 |
PFN_DOWN(4096) | 1 | 进入第 1 页 |
PFN_DOWN(0x100000) | 256 | 1MB 地址对应第 256 页 |
配套宏
PFN_DOWN通常与以下宏配合使用:
| 宏 | 作用 | 示例 |
|---|---|---|
PFN_DOWN(x) | 地址 → 页帧号(向下取整) | PFN_DOWN(4095) = 0 |
PFN_UP(x) | 地址 → 页帧号(向上取整) | PFN_UP(4095) = 1 |
PFN_PHYS(x) | 页帧号 → 物理地址 | PFN_PHYS(1) = 4096 |
PFN_ALIGN(x) | 地址向上对齐到页边界 | PFN_ALIGN(4095) = 4096 |
它们之间的关系可以用一个公式概括:
PFN_PHYS(PFN_DOWN(addr)) <= addr < PFN_PHYS(PFN_UP(addr))
内核中的典型用途
建立页帧号与
struct page的映射:struct page *page = pfn_to_page(PFN_DOWN(phys_addr));
计算内存区域包含的页数:
nr_pages = PFN_DOWN(end) - PFN_DOWN(start);
DMA 和内存映射:将设备物理地址转换为页帧号,以便进行内存管理操作。
使用注意
PFN_DOWN是向下取整,意味着地址落在某页的任意位置,都返回该页的页帧号。如果需要向上取整(例如计算覆盖某段内存所需的页数),应使用
PFN_UP。该宏假设输入是物理地址,不适用于虚拟地址。虚拟地址到页帧号的转换需要使用
virt_to_pfn等函数。