Linux虚拟地址按字节对齐:PAGE_ALIGN(addr)

本文探讨了Linux中的内存对齐机制,重点介绍了PAGE_ALIGN宏如何将地址调整到4096字节的边界。通过示例解释了(PAGE_SIZE-1)与~(PAGE_SIZE-1)在地址对齐过程中的作用,以及PAGE_SIZE在不同平台上的配置,特别是ARM64架构下的实现。

本文参考Linux-4.1.15

Linux通过PAGE_ALIGN(addr)将addr加大对齐到4096字节的边界(4096的整数倍),假设addr=4096+n,{0<n<4096},则执行PAGE_ALIGN(addr)后,addr=4096+4096

PAGE_ALIGN这个宏在include/linux/mm.h中,

/* to align the pointer to the (next) page boundary */
#define PAGE_ALIGN(addr)  ALIGN(addr, PAGE_SIZE)

同时Linux中如下定义:

#define ALIGN(x, a)        __ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a)        __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask)    (((x) + (mask)) & ~(mask))

PAGE_ALIGN(addr)等价于:(addr+(PAGE_SIZE-1)) & ~(PAGE_SIZE-1)

假设PAGE_SIZE=4096,即1 0000 0000 0000(b),则(PAGE_SIZE-1) = 1111 1111 1111(b),~(PAGE_SIZE-1)=0000 0000 0000(b)。

由上可知,一个数为4096的整数倍,则这个数的二进制的低12位等于0。对于(addr+(PAGE_SIZE-1)) & ~(PAGE_SIZE-1)来说,&左边将addr加大到下一个4096字节范围,&右边则将addr低12清零(对齐)。

另外,关于PAGE_SIZE的定义如下,linux-4.1.15\arch\arm64\include\asm\page.h

/* PAGE_SHIFT determines the page size */
#ifdef CONFIG_ARM64_64K_PAGES
#define PAGE_SHIFT        16
#else
#define PAGE_SHIFT        12
#endif
#define PAGE_SIZE     (_AC(1,UL) << PAGE_SHIFT)//_AC(1,UL)=1UL=1个unsigned long,最终为4KB或64KB
#define PAGE_MASK        (~(PAGE_SIZE-1))

当然,PAGE_SIZE的值本质是由ARM SOC寄存器的配置决定的。

### PAGE_ALIGN的含义及其在内存管理中的作用 PAGE_ALIGN 是一种用于实现页面对齐的宏定义,常见于操作系统内核(如 Linux 和 Windows)中,用于确保内存地址按照页面大小对齐。在现代系统中,内存通常以页(page)为单位进行管理,常见的页面大小为 4KB(即 4096 字节)。为了提高内存访问效率和简化地址转换,通常要求内存地址对齐到页面边界,即地址为 PAGE_SIZE 的整数倍。 PAGE_ALIGN 的作用是将一个给定的地址向上对齐到最近的页面边界。这在内存分配、虚拟地址映射、页表操作等场景中具有重要意义。例如,在内核中分配内存时,若请求的地址未对齐到页面边界,则需要通过 PAGE_ALIGN 宏将其调整为最近的页面对齐地址,以确保后续操作的正确性和性能[^2]。 ### PAGE_ALIGN 的原理 PAGE_ALIGN 的实现基于位运算,其核心思想是利用页面大小的特性(即 PAGE_SIZE 通常是 2 的幂次)来快速完成对齐操作。以 PAGE_SIZE = 4096 为例,其二进制表示为 `0x1000`,即 `1000000000000`。因此,一个地址若为页面对齐的,则其低 12 位为 0。 PAGE_ALIGN 的典型实现如下: ```c #define PAGE_MASK (~(PAGE_SIZE - 1)) #define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) ``` 该宏的执行过程如下: 1. `(addr) + PAGE_SIZE - 1`:将地址加上 PAGE_SIZE - 1,确保在向下取整时能够跳过当前页面未对齐的部分。 2. `& PAGE_MASK`:通过与 PAGE_MASK 进行按位与操作,清除低 12 位,从而将地址对齐到最近的页面起始地址。 例如,若 `addr = 0x1234`,则 `PAGE_ALIGN(addr)` 的结果为 `0x2000`,因为 `0x1234 + 0xFFF = 0x2233`,再与 `0xFFFFF000` 进行按位与操作后结果为 `0x2000`。 这种实现方式不仅高效,而且具有良好的可移植性,适用于不同架构下的页面大小配置[^4]。 ### 内存对齐的作用 1. **提高内存访问效率**:现代 CPU 对内存的访问是以缓存行为单位进行的,若数据未对齐到页面边界,可能会导致多次内存访问,从而影响性能。 2. **简化地址转换**:在虚拟内存系统中,页表机制依赖于页面对齐的地址进行映射和转换。若地址未对齐,将增加页表管理的复杂度。 3. **保证内存操作的原子性**:某些内存操作(如原子变量、自旋锁等)要求数据结构在内存中对齐到特定边界,以确保操作的原子性和一致性。 4. **避免硬件限制**:部分硬件(如 DMA 控制器)在访问内存时要求数据必须对齐到页面边界,否则可能引发异常或性能下降。 ### 示例代码 以下是一个使用 PAGE_ALIGN 宏的示例: ```c #include <stdio.h> #define PAGE_SIZE 4096 #define PAGE_MASK (~(PAGE_SIZE - 1)) #define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) int main() { unsigned long addr = 0x1234; unsigned long aligned_addr = PAGE_ALIGN(addr); printf("Original address: 0x%lx\n", addr); printf("Aligned address: 0x%lx\n", aligned_addr); return 0; } ``` 输出结果为: ``` Original address: 0x1234 Aligned address: 0x2000 ``` 该示例展示了如何将一个非对齐地址转换为最近的页面对齐地址。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值