内存对齐的一个宏:#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
以前对于这个宏有讨论过,不过似乎都讲得很模糊,作为一个知识点不应该有模糊
这个宏的目的是:将地址值调整为下一个边界上。
先问问自己:我应该怎么去实现这个功能呢?
假设: addr = 0xbfdf8cf3 也就是:1011 1111 1101 1111 1000 1100 1111 0011
那么:如果我要将这个值调整到下一个2字节边界上
就应该是:xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx10
也就是2的倍数上。
如果我要将这个值调整到下一个4字节边界上
就应该是:xxxx xxxx xxxx xxxx xxxx xxxx xxxx x100
也就是4的倍数上。
最后,如果我要将这个值调整到下一个8字节界边上
就应该是:xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1000
也就是8的倍数上
怎么做呢? 我必须根据所要调整边界的要求 将 addr 的低几位置 0,同时还要增加一个 模值
例如:我要将 addr 调整到 4 字节界上,于是我就有一个初步的想法
1、addr 加上 0100 使它跳到下一个 4 字节
addr + 0000 0000 0000 0000 0000 0000 0000 0100
结果为:1011 1111 1101 1111 1000 1100 1111 0111
2、将上一步的结果低 2 位清 0,从而实现调整要求
1011 1111 1101 1111 1000 1100 1111 0111
& 1111 1111 1111 1111 1111 1111 1111 1100
--------------------------------------------------------------
1011 1111 1101 1111 1000 1100 1111 0100
3、1111 1111 1111 1111 1111 1111 1111 1100 怎么通过 4 运算得出来呢?
负数:-4 在机器中表示为: -4 = ~4 + 1
即: 1111 1111 1111 1111 1111 1111 1111 1011 = ~4
+ 0000 0000 0000 0000 0000 0000 0000 0001 = 1
-----------------------------------------------------------------------------
1111 1111 1111 1111 1111 1111 1111 1100 = -4
4 、总结一下,得出以下算法
(addr + align_size) & (-align_size)
所以:我可以这样定义这个宏
#define PAGE_ALIGN(addr, align_size) ((addr+align_size) & (-align_size))
而后来:我发现,只要加上被清0位数上的最大值,就可以跳到下一个对齐边界上,
也就是: 4 字节对齐时,只要加上3就可以了,
8 字节对齐时,只要加上7就可以了,
2 字节对齐时,只要加上1就可以了,
即: addr + align_size - 1 就可以了 !!!
下一步做 & 时:4字节对齐,只要将 3 取反就得出 0xFFFF FFF4
8字节对齐,只要将 7 取反就得出 0xFFFF FFF8
2字节对齐,只要将 1 取反就得出 0xFFFF FFF2
于是: 我就有了算法的另一个版本:
( addr + align_size - 1 ) & (~(align_size - 1))
最后, 我就可以将宏定义为:
#define PAGE_ALIGN(addr, align_size) ((addr+align_size-1) & ~(align_size-1))
也就是,LZ 所问的宏是怎么来的
补充一点:
(addr + align_size) & (-align_size)
这个算法,忽略了对 低几位是 0 的情况的考虑!
如果:xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx00
这种情况的话 addr + align_size 导致空跳到下一边界
addr + align_size - 1 无论什么情况下都是正确的。
-align_size 与 ~ (align_size - 1) 本质上是一样的, 而 - align_size 似乎更好理解,建议采用这种形式