Ядро Linux в комментариях

       

Pte_alloc


Функция 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. В испытаниях, проведенных автором, оба эти варианта показали почти одинаковую скорость.)


Независимо от используемого метода, после этих вычислений сложение адреса с базовым адресом его таблицы PMD в формате unsigned long (что происходит в строке и в другом месте) позволяет определить вход для этого указателя в таблице РТЕ, связанной с первоначальным значением адреса.

Если этот вход PMD не указывает ни на одну таблицу страниц, эта функция выполняет переход вперед, к метке getnew, для ее распределения.

Попытка выбрать таблицу страниц из списка pte_quicklist с помощью вызова функции get_pte_fast (строка ). Это кэш таблиц страниц — идея состоит в том, что распределение таблиц страниц (которые сами являются просто отдельными страницами) происходит медленно, но распределение памяти из списка недавно освобожденных таблиц страниц происходит немного быстрее. Поэтому в коде часто выполняется освобождение таблиц страниц с помощью функции free_pte_fast (строка ), которая переводит таблицы страниц в список pte_quicklist, a не освобождает их на самом деле.

Список pte_quicklist смог предоставить страницу для таблицы страниц. Таблица страниц введена в промежуточный каталог страниц и функция возвратила смещение этой страницы в таблице страниц.

В кэше pte_quicklist не осталось ни одной страницы, поэтому функция pte_alloc вынуждена распределить страницу медленным способом, вызвав функцию get_pte_slow (строка ). Эта функция распределяет страницу с помощью функции __get_free_page и выполняет во многом такую же обработку, как и при получении страницы из кэша.

Если вход PMD не равен 0, но является недействительным, функция pte_alloc выводит сообщение (с помощью вызова функции bad_pte, строка ) и отвергает попытку.

Обычный случай, на который мы рассчитываем: функция pte_alloc возвращает указатель на таблицу РТЕ, которая содержит адрес.


Содержание раздела