Функция pte_alloc получает два параметра: указатель на вход промежуточного каталога страниц, где находится желаемый адрес, и сам адрес. Извращенную логику этой функции будет проще объяснить, если мы приступим к ее описанию не сразу, поэтому пройдем по следующим номерам строк.
Преобразует адрес в смещение в PMD почти непостижимым способом.
Эта строка заслуживает подробного описания. Для начала напомним, что каждый вход в PMD представляет собой указатель, а указатели в архитектуре х86 занимают 4 байта (здесь мы находимся в части кода, зависящей от архитектуры, поэтому можем делать такие предположения). К тому же, мы знаем из определения языка С, что строка
&pmd[middle_10_bits(address)]
(здесь автор для ясности ввел фиктивный массив pmd и функцию middle_10_bits— средние десять битов) эквивалентна
pmd + middle_10_bits(address)
которая, в свою очередь, представляет тот же адрес, что и
((char *) pmd) + middle_10_bits(address) * sizeof(pte_t*)
Здесь весь фокус состоит в том, чтобы понять, что эта последняя форма или, если быть точнее, та ее часть, которая следует за знаком +, больше всего напоминает команду, которая фактически вычисляется в строке .
Чтобы убедиться в этом, вначале отметим, что
4 * (PTRS_PER_PTE - 1)
равно 4092 (величина PTRS_PER_PTE установлена директивой #define равной 1024 в строке ). В двоичном коде в числе 4092 установлены только младшие 12 битов, не считая последних двух. Это равно числу 1023, в котором установлены только 10 младших битов, сдвинутому влево на 2 бита. Затем имеется команда
(address >> (PAGE_SHIFT - 2))
которая сдвигает адрес на 10 битов вправо (величина PAGE_SHIFT установлена директивой #define равной 12 в строке ). Затем над этими двумя выражениями выполняется поразрядная операция AND. Чистый результат выглядит так:
((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) << 2
Хотя это выражение все еще выглядит сложным, уже многое прояснилось: в нем адрес сдвигается на 12 битов вправо (для удаления части со смещением страницы), маскируются все биты, кроме младших 10 (для удаления части с индексом каталога страниц и сохранения только индекса промежуточного каталога страниц в младших 10 битах), а затем выполняется сдвиг полученного результат на 2 бита влево (что равносильно умножению на 4 — на число байтов в указателе — sizeof(pte_t*)). Однако более простой способ был бы, вероятно, немного медленнее, а в программировании ядра мы экономим циклы процессора везде, где это возможно. (Однако простой способ внешне не выглядит как более медленный: в версии ядра выполняются два сдвига, два вычитания и поразрядная операция AND, а в методе автора выполняются два сдвига и две поразрядные операции AND. В испытаниях, проведенных автором, оба эти варианта показали почти одинаковую скорость.)