Это распределение указатель блок страницы с выравниванием по вертикали, то есть PAGES
количество страниц с использованием диспетчеров C++ вместо специальных выделенных выделенных функций выделения (например, POSIX's posix_memalign
or C11's aligned_alloc
).
Сначала он выделяет PAGES + 1
страниц с памятью (это может быть или не выравниваться по странице), затем корректирует полученный указатель вперед так, чтобы он указывал на выровненный по первой строке байт в результате. Пополняя дополнительную страницу, он знает, что у нее, безусловно, будет достаточно большое выделение, чтобы за ней можно было использовать PAGES
страниц. Программа просто должна быть уверена, что delete
s mem
, когда это будет сделано, а не memAligned
(удаление последнего, вероятно, приведет к сбою программы сейчас, позже из-за повреждения кучи или просто утечки памяти, это неопределенное поведение, поэтому таяние вашего компьютера на шлак является юридическим поведением).
Эта последняя строка численно эквивалентна округлению до следующего кратного размера страницы; он добавляет PAGE_SIZE - 1
к указателю (поэтому, если указатель уже выровнен по странице, он все еще находится на той же странице, иначе он перемещается на следующую страницу), а затем маскирует низкие биты адреса (что отменяет добавление в «уже page aligned ", а во всех остальных случаях сбрасывает указатель на начало первой страницы, следующей за неузнанным указателем, в mem
).
Детали: ~
является побитовое инвертирование, так ADDR_MASK
, что, вероятно, что-то вроде 0x00000FFF
для 4096 байт страниц, становится 0xFFFFF000
(листать все биты). Когда &
-в значение, сохраняются только биты, установленные в обоих операндах. Чтобы привести примеры: для 32-битного указателя мы предположим, что new
дал нам 0xDEADBEEF
, а PAGE_SIZE
- 4096. Добавление на 4095 (0xFFF
) означает, что у нас есть «0xDEADCEEE». Затем мы маскируем 0xFFFFF000
, что устраняет низкие разряды, давая нам 0xDEADC000
, адрес, выровненный по первой странице, следующий за 0xDEADBEEF
. То же самое произойдет с любым адресом, не привязанным к странице, который был возвращен new
.
Если значение уже страница выровнены, хотя, скажем, 0xDEADB000
, добавив на 4095/0xFFF
получает нас 0xDEADBFFF
(обратите внимание, как никаких битов в 0xDEADB
не изменились), поэтому, когда мы маскировать, чтобы получить выровненный адрес, мы вернемся 0xDEADB000
снова , так как мы уже выровнены по странице.
Присвоение uintptr_t
заключается в том, чтобы мы могли манипулировать адресом с помощью математических операторов и гарантировать, что побитовое инвертирование заполняет все биты, необходимые для соответствия указателю (если он не имеет размера должным образом, вы можете инвертировать, затем преобразовывать с повышением и вдруг у вас будет куча нулей слева, а не только справа, и вы в конечном итоге замаскируете важные биты в указателе, поэтому это указывает на совершенно другое и неправильное место).
TL; DR: он отнимает память и вам не нужен такой код :) –
@KubaOber: Есть некоторые ограниченные обстоятельства, при которых выровненная страница памяти стоит отходов; некоторые ОС могут выполнять оптимизацию для уменьшения копий памяти, когда данные считываются/записываются в/из выровненной страницы. Если вам стоит одна страница памяти для получения нулевой копии передачи данных на веб-сервере с высокой нагрузкой, это того стоит. – ShadowRanger
@ShadowRanger Я не говорю, что вы не должны выровнять свою память, просто потому, что вы должны сделать это, запросив явно выровненную строку. В конце концов, поскольку «СТРАНИЦЫ» являются «большими», распределитель, лежащий в основе «нового», попросит ОС для хранения. Таким образом, вы также можете сделать это сами и получить таким образом хранилище с выравниванием по страницам. Во многих или даже большинстве случаев «mem» будет уже выровнен, и тогда вы, возможно, увеличите «СТРАНИЦЫ», если это не будет const, и избегайте отходов, но я не думаю, что такие хаки следует поощрять. –