PFN_UP是PFN_DOWN的配套宏,用于将物理地址向上取整转换为页帧号(PFN)。定义在include/linux/pfn.h中。
定义与原理
#define PFN_UP(x) (((x) + PAGE_SIZE - 1) >> PAGE_SHIFT)
它的核心逻辑是先将地址加上PAGE_SIZE - 1,再右移PAGE_SHIFT位,相当于向上取整除法。
在 x86 架构下,PAGE_SHIFT为 12,PAGE_SIZE为 4096,因此:
PFN_UP(addr) = (addr + 4095) / 4096
核心作用
将物理地址向上取整到它所在的下一个页帧号。如果地址恰好落在页边界上,则返回该页的页帧号;否则返回下一页的页帧号。
| 表达式 | 结果 | 说明 |
|---|---|---|
PFN_UP(0) | 0 | 恰好是页边界,返回第 0 页 |
PFN_UP(1) | 1 | 跨越了第 0 页,向上取整到第 1 页 |
PFN_UP(4095) | 1 | 仍在第 0 页内,向上取整到第 1 页 |
PFN_UP(4096) | 1 | 恰好是页边界,返回第 1 页 |
PFN_UP(4097) | 2 | 跨越了第 1 页,向上取整到第 2 页 |
与PFN_DOWN的对比
| 宏 | 行为 | 示例 |
|---|---|---|
PFN_DOWN(x) | 向下取整,返回地址所在页的 PFN | PFN_DOWN(4095) = 0 |
PFN_UP(x) | 向上取整,返回地址之后的页的 PFN | PFN_UP(4095) = 1 |
两者的关系可以用一个不等式表示:
PFN_DOWN(addr) <= PFN_UP(addr)
内核中的典型用途
PFN_UP最常见的场景是计算一段内存区域跨越的页数,尤其是当起始或结束地址不在页边界上时:
计算覆盖某段内存所需的页数:
nr_pages = PFN_UP(end) - PFN_DOWN(start);
确保分配的内存覆盖请求范围:
如果请求的大小不是页大小的整数倍,PFN_UP会确保分配的页数足够覆盖整个请求。DMA 缓冲区计算:
设备 DMA 操作通常要求缓冲区按页对齐,PFN_UP用于确定实际需要映射的页数。
使用注意
PFN_UP是向上取整,意味着即使地址只多出 1 字节,也会算作新的一页。该宏假设输入是物理地址,不适用于虚拟地址。
在计算页数时,通常需要
PFN_UP(end) - PFN_DOWN(start),而不是简单的PFN_UP(end) - PFN_UP(start),否则可能少算一页。