ROUND_UP 宏依靠整数除法来完成工作。它仅在两个参数都是整数时才有效。我假设 N 是要舍入的数字,S 是应该舍入的间隔。也就是说,ROUND_UP(12, 5) 应该返回 15,因为 15 是大于 12 的 5 的第一个区间。
想象一下我们是向下取整而不是向上取整。在这种情况下,宏将是:
#define ROUND_DOWN(N,S) ((N / S) * S)
ROUND_DOWN(12,5) 将返回 10,因为整数除法中的(12/5) 是 2,而 2*5 是 10。但我们不是在做 ROUND_DOWN,而是在做 ROUND_UP。因此,在进行整数除法之前,我们希望在不损失准确性的情况下尽可能多地相加。如果我们添加S,它几乎适用于所有情况; ROUND_UP(11,5) 会变成 (((11+5) / 5) * 5),因为整数除法的 16/5 是 3,所以我们会得到 15。
当我们传递一个已经四舍五入到指定倍数的数字时,问题就来了。 ROUND_UP(10, 5) 会返回 15,这是错误的。因此,我们不添加 S,而是添加 S-1。这保证了我们永远不会将某些东西不必要地推到下一个“桶”。
PAGE_ 宏与二进制数学有关。为简单起见,我们假设我们正在处理 8 位值。假设PAGE_SIZE 是0b00100000。 PAGE_SIZE-1 因此是 0b00011111。 ~(PAGE_SIZE-1) 是 0b11100000。
二进制& 将排列两个二进制数,并在两个数都为 1 的地方留下 1。因此,如果 x 是 0b01100111,则操作将如下所示:
0b01100111 (x)
& 0b11100000 (~(PAGE_SIZE-1))
------------
0b01100000
您会注意到,该操作实际上仅将最后 5 位归零。就这样。但这正是需要向下舍入到最接近的间隔PAGE_SIZE 的操作。请注意,这只有效,因为PAGE_SIZE 恰好是 2 的幂。这有点像说对于任意十进制数,只需将最后两位数归零即可向下舍入到最接近的 100。它工作得很好,而且很容易做到,但如果你试图四舍五入到最接近的 76 倍数,它就根本不起作用。
PAGE_ROUND_UP 做同样的事情,但它会在页面中添加尽可能多的内容,然后再将其切断。这有点像我如何通过将 99 添加到任何数字并 然后 将最后两位数归零来四舍五入到最接近的 100 倍数。 (我们添加PAGE_SIZE-1 的原因与我们在上面添加S-1 的原因相同。)
祝你的虚拟内存好运!