Функция get_vm_area пытается возвратить свободный участок в диапазоне от VMALLOC_START до VMALLOC_END. Обычно она выполняет это от имени vmalloc; эта функция используется также в нескольких других случаях, которые здесь не рассматриваются. Вызывающая функция отвечает за обеспечение того, что параметр size является ненулевым кратным размера страницы. Функция vmalloc работает по так называемому алгоритму «первого подходящего», поскольку она возвращает указатель на первый же найденный блок, который удовлетворяет запросу. Существуют также алгоритмы «наиболее подходящего», которые распределяют память из наименьшей доступной свободной области, которая достаточно велика для выполнения запроса, и алгоритмы «наихудшего подходящего», которые всегда распределяют память из наибольшей доступной свободной области. Распределитель памяти каждого типа имеет свои преимущества и недостатки, но реализованный здесь алгоритм «первого подходящего» является простым и быстрым и вполне соответствует своему назначению.
Распределение объекта struct vm_struct для представления новой области. Распределяемые области отслеживаются с помощью отсортированного связанного списка vmlist (строка ) объектов struct vm_struct. Файл заголовка, к которому принадлежит struct vm_struct, был опущен в целях экономии места, но определение этого объекта является весьма простым:
struct vm_struct { unsigned long flags; void * addr; unsigned long size; struct vm_struct * next; };
Каждый элемент списка связан с одним распределенным блоком памяти, как показано на рис.8.6. Можно видеть, что задача функции get_vm_area состоит в поиске достаточно широкого промежутка между распределенными областями.
8.6. Список vmlist
Начинается циклический просмотр списка. Цикл должен либо найти достаточно большую свободную область, либо показать, что такой области не существует. В нем вначале проверяется VMALLOC_START, а затем адрес, непосредственно следующий за каждой распределенной областью.
Список был пуст или в цикле была обнаружена достаточно большая область для нового блока; так или иначе, addr теперь представляет собой наименьший доступный адрес. Заполняется новый объект struct vm_struct, который и будет возвращен.
Добавление страницы (размером 4 Кб в архитектуре х86) к размеру зарезервированного блока для перехвата выхода за пределы ядра в направлении старших адресов памяти и, возможно, выхода в направлении младших адресов памяти из следующего по порядку блока. Поскольку это дополнительное пространство не учтено при определении того, было ли достаточно велико текущее отверстие (строка ), область, зарезервированная этим входом, может перекрывать следующую, и выход за пределы памяти ядра в эту «лишнюю» область в направлении старших адресов может в действительности перезаписать распределенную память. Правильно? Неправильно. Легко доказать, что значение addr всегда выровнено по странице, и мы уже знаем, что параметр size всегда кратен размеру страницы. Поэтому, если величина size + addr меньше начального адреса следующей области, она должна быть меньше, по крайней мере, на целую страницу. Безусловно, выход за пределы памяти более чем на одну страницу может затронуть следующую область, но выход за пределы памяти менее чем на одну страницу — нет. Поскольку ядро не устанавливает отображение страницы для этой дополнительной памяти, ошибочная попытка обратиться к ней вызовет неразрешимую ситуацию отсутствия страницы (что является почти неслыханным в современных версиях Linux!). В результате ядро перейдет в состояние жесткого останова, но это лучше, чем позволить ядру молча крушить его собственные структуры данных. По крайней мере, вы сразу же узнаете о тяжелом останове, что позволит вам диагностировать проблему; последний же сценарий не станет очевидным до тех пор, пока ядро не разгромит все внешние устройства.