Linux Kernel (Ядро линукса) (часть 1)


Постоянные отображения

Постоянные отображения позволяют ядру установить долговременное отображение страничных кадров верхней памяти в адресное пространство ядра. Они используют специально отведенную для этих целей Таблицу Страниц из числа главных таблиц страниц ядра. Переменная pkmap page tabie хранит адрес этой Таблицы Страниц, а макрос last_pkmap возвращает количество записей. Как обычно, Таблица Страниц включает в себя либо 512, либо 1024 записей, в зависимости от того, задействован ли механизм РАЕ. В результате ядро может одновременно адресовать максимум два или четыре мегабайта верхней памяти.
Таблица Страниц отображает линейные адреса, начиная с pkmap base. Массив pkmap count содержит last pkmap счетчиков, по одному на каждую запись Таблицы Страниц pkmap page tabie.

Мы различаем три случая:

- счетчик равен 0 — соответствующая запись Таблицы Страниц не отображает никакой страничный кадр верхней памяти и может быть использована;
- счетчик равен 1 — соответствующая запись Таблицы Страниц не отображает никакой страничный кадр верхней памяти, но использовать ее нель
зя, потому что соответствующая запись TLB-буфера не была очищена с момента последнего использования;
- счетчик равен п (больше 1) — соответствующая запись Таблицы Страниц отображает страничный кадр верхней памяти, которым пользуются n-1 компонентов ядра.

Для отслеживания связи между страничными кадрами верхней памяти и линейными адресами, установленной постоянным отображением, ядро пользуется хеш-таблицей page address htabie. Эта таблица содержит по одной структуре page address map для каждого страничного кадра верхней памяти, отображенного в данный момент. В свою очередь, каждая структура содержит указатель на дескриптор страницы и линейный адрес, присвоенный страничному кадру.

Функция page address о возвращает линейный адрес, ассоциированный со страничным кадром, или null, если страничный кадр находится в верхней памяти, но не отображен. Эта функция, принимающая в качестве параметра указатель на дескриптор страницы раде, различает два случая:
- Если страничный кадр не расположен в верхней памяти (флаг PG highmem сброшен), линейный адрес существует всегда и получается в результате вычисления индекса страничного кадра, преобразования его в физический адрес и вычисления линейного адреса, соответствующего физическому. Все это выполняется в следующем фрагменте кода:va ( (unsigned long) (page — mem_map) « 12)
- Если страничный кадр находится в верхней памяти (флаг PG highmem установлен), функция рассматривает хеш-таблицу page address htabie. Если страничный кадр обнаруживается в хеш-таблице, функция page address о возвращает его линейный адрес; в противном случае она возвращает null.
Функция kmapo устанавливает постоянное отображение. Она эквивалентна следующему коду:
void kmap(struct page page)
{if (!PageHighMem(page))
return page_address(page);
return kmap_high(page);}
Функция kmap high () вызывается, если страничный кадр действительно принадлежит верхней памяти. Она эквивалентна следующему коду:
void kmap_high(struct page page){
unsigned long vaddr;
spin_lock(&kmap_lock);
vaddr = (unsigned long) page_address(page); if (!vaddr)
vaddr = map_new_virtual(page); pkmap_count[(vaddr-PKMAP_BASE) » PAGE_SHIFT]++; spin_unlock(&kmap_lock); return (void ) vaddr;
}
Эта функция получает спин-блокировку kmap iock для защиты Таблицы Страниц от попыток одновременного обращения в многопроцессорных системах. Обратите внимание на отсутствие необходимости отключать прерывания, поскольку функция kmap не может быть вызвана обработчиком прерывания или функцией отложенного выполнения. Функция kmap higho затем проверяет, отображен ли уже страничный кадр, для чего вызывает функцию page address о. Если ответ отрицательный, функция вызывает функцию map new virtuai о, чтобы занести физический адрес страничного кадра в запись таблицы pkmap page tabie и добавить элемент в хеш-таблицу page address htable. После ЭТОГО функция kmap_high увеличивает счетчик, соответствующий линейному адресу страничного кадра, чтобы учесть новый компонент ядра, вызвавший эту функцию. Наконец, функция kmap high освобождает спин-блокировку kmap iock и возвращает линейный адрес, отображающий страничный кадр.

Функция map new virtuai выполняет два цикла, один внутри другого:
for (;;) {int count;
DECLARE JWAITQUEUE(wait, current);
for (count = LAST_PKMAP; count > 0; —count) {
last_pkmap_nr = (last_pkmap_nr + 1) & (LAST_PKMAP — 1) ; if (!last_pkmap_nr) {
flush_all_zero_pkmaps () ; count = LAST_PKMAP;}
if (! pkmap_count [ 1 ast_pkmap_nr ] ) {
unsigned long vaddr = PKMAP_BASE +
(last_pkmap_nr « PAGE_SHIFT) ; set_pte (& (pkmap_page_table [last_pkmap_nr] ) ,
mk_pte (page, pgprot (0x63) ) ) ;
pkmap_count[last_pkmap_nr] = 1; set_page_address(page, (void ) vaddr); return vaddr;}}
current->state = TASK_UNINTERRUPTIBLE; add_wait_queue (&pkmap_map_wait, &wait); spin_unlock (&kmap_lock) ; schedule();
remove_wait_queue (&pkmap_map_wait, &wait) ; spin_lock (&kmap_lock) ; if (page_address(page))
return (unsigned long) page_address(page);}
Во внутреннем цикле функция перебирает все счетчики в массиве pkmap count, пока не найдет счетчик с нулевым значением. Большой блок if выполняется, когда в массиве pkmap count обнаруживается неиспользуемый элемент. Этот блок определяет линейный адрес, соответствующий данному элементу, создает для него запись в Таблице Страниц pkmap page tabie, устанавливает счетчик в 1, поскольку теперь запись используется, вызывает функцию set page address о, чтобы занести новый элемент в хеш-таблицу page address htabie, и возвращает линейный адрес.

Функция начинает поиск с того места, где закончила последний раз, перебирая элементы массива pkmap count. Она может это сделать, потому что сохранила в переменной last pkmap nr индекс последней использованной записи в Таблице Страниц pkmap page tabie. Итак, поиск начинается с того места, где он был закончен при последнем вызове функции map new virtuai.

Когда достигнут последний элемент массива pkmap count, поиск возобновляется со счетчика с индексом 0. Однако прежде чем продолжить, функция map_new_virtuai о вызывает функцию fiush_aii_zero_pkmaps (), которая начинает свой перебор счетчиков в поисках тех, значение которых равно единице. Каждый счетчик с единичным значением соответствует записи в таблице pkmap page tabie, которая свободна, но не может быть использована из- за того, что связанный с ней элемент TLB-буфера еще не был очищен. Функция flush aii zero pkmaps о записывает нули в эти счетчики, удаляет соответствующие элементы из хеш-таблицы page address htabie и выполняет сброс элементов TLB-буфера на диск для всех записей таблицы
р kmap_pa ge_t able.
Если по ходу внутреннего цикла счетчик с нулевым значением не был найден в массиве pkmap count, функция map new virtuai блокирует текущий процесс, пока какой-нибудь другой процесс не освободит запись в Таблице Страниц pkmap page tabie. Это достигается путем постановки процесса current в очередь pkmap map wait, установки его в состояние TASK_ UNINTERRUPTIBLE И ВЫЗОВе фунКЦИИ schedule , Чтобы уступить Процессор другим задачам. После пробуждения процесса функция проверяет, отобразил
ли другой процесс страницу, для чего вызывает функцию page_address о. Если никакой процесс еще не отобразил эту страницу, возобновляется внутренний цикл.

Функция kunmapO отменяет постоянное отображение, ранее установленное функцией kmap . Если страница действительно находится в зоне верхней памяти, функция вызывает функцию kunmap high , которая эквивалентна следующему коду:
void kunmap_high(struct page page)
{spin_lock(&kmap_lock);
if ((—pkmap_count[((unsigned long)page_address(page)
-PKMAP_BASE)» PAGE_SHIFT]) == 1) if (waitqueue_active (&pkmap_map_wait) ) wake_up (&pkmap_map_wait) ; spin_unlock(&kmap_lock);}
Выражение в скобках вычисляет индекс элемента в массиве pkmap count по линейному адресу страницы. Счетчик уменьшается и сравнивается с 1. Равенство означает, что ни один процесс не использует страницу. Функция может разбудить процессы в очереди, которую заполнила функция map new virtual , если таковые имеются.

Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Следующая страница




Возможно, Вас также заинтересует:

ОС Knoppix - это Linux без проблем

ВведениеЕсли вы цените свое время, умеете считать деньги и знаете стоимость информации, то эта книга для вас. А так как к книге прилагается компакт- диск с готовой к работе операционной системой Knoppix Live CD, то лишь достаточно вставить его в привод и перегрузить компьютер,...

Linux Kernel (Ядро линукса) (часть 1)

Спин-блокировкаСпин-блокировка необходима в многопроцессорной системе, потому что могут возникнуть другие прерывания того же типа, и другие процессоры могут приступить к их обработке. Без спин-блокировки к главному дескриптору прерывания могли бы обратиться сразу несколько процессоров. Как мы...

Linux Kernel (Ядро линукса) (часть 2)

Копирование при записи В системах Unix первых поколений создание процесса было реализовано довольно неуклюже: получив системный вызов fork о, ядро в буквальном смысле дублировало все адресное пространство родителя и присваивало копию процессу-потомку. Такая операция...

Linux Kernel (Ядро линукса) (часть 3)

Буферы блоков и головы буферовУ каждого буфера есть дескриптор голова буфера, имеющий тип buffer head. Этот дескриптор содержит всю информацию, необходимую ядру для работы с блоком, так что перед обработкой блока ядро обязательно проверяет голову...