Макрос ROUND_UP
полагается на целочисленное деление для выполнения работы. Это будет работать, только если оба параметра являются целыми числами. Я предполагаю, что N
- это число, которое нужно округлить, а S
- это интервал, на котором оно должно быть округлено. То есть ROUND_UP(12, 5)
должно возвращать 15, так как 15 - первый интервал 5 больше 12.
Представьте, что мы округляем, а не вверх. В этом случае макрос будет просто:
#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
делает то же самое, но добавляет на страницу столько, сколько может, перед тем как обрезать ее. Это похоже на то, как я могу округлить до ближайшего кратного 100, добавив 99 к любому числу и , затем обнуляя последние две цифры. (Мы добавляем PAGE_SIZE-1
по той же причине, что мы добавили S-1
выше.)
Удачи с вашей виртуальной памятью!