netlib.narod.ru | < Назад | Оглавление | Далее > |
Одним из интересных аспектов программирования ядра является необходимость обходиться без многих вспомогательных функций, которые прикладные программисты обычно считают само собой разумеющимися, например, без функций malloc и free библиотеки С, которые основаны на примитиве brk ядра.
Автор допускает, что код ядра можно было бы пересмотреть для увязки со стандартной библиотекой С и использования ее функций malloc и free, но конечный результат стал бы и неуклюжим, и медленным — предполагается, что вызов этих функций происходит в режиме пользователя, поэтому ядру для их вызова приходилось бы переключаться в режим пользователя, а этим функциям затем пришлось бы выполнять противоположную задачу и направлять прерывания в ядро, которому пришлось бы как-то следить за тем, что происходит, и т.д. Для того, чтобы можно было обойтись без всех этих сложностей, в ядро включены его собственные версии многих знакомых функций, в том числе функций malloc и free.
И действительно, в ядре предусмотрены две отдельные пары функций, аналогичные malloc и free. Первая пара, kmalloc и kfree, управляет памятью, которая была распределена в самом сегменте ядра — фрагментами реальной, физической памяти, чьи фактические адреса известны. Вторая пара, vmalloc и vfree, распределяет и освобождает виртуальную память для нужд ядра. Память, возвращенная функцией kmalloc, лучше приспособлена для таких целей, как размещение драйверов устройств, поскольку они всегда присутствуют в физической памяти и, вместе с тем, занимают физически непрерывный участок. Однако функция kmalloc пользуется намного более ограниченными ресурсами, чем vmalloc, поскольку vmalloc может получать место в пространстве свопинга.
Функции vmalloc и vfree частично реализованы по принципам kmalloc и kfree, поскольку им нужно немного невыгружаемой памяти для ведения учета. Функции kmalloc и kfree, с другой стороны, реализованы на основе __get_free_pages, free_pages и других функций нижнего уровня для манипулирования страницами памяти.
Автор здесь не рассматривает kmalloc и kfree, но этот код включен для вашего пользования (см. соответственно, строки 37043 и 37058). Вместо этого, ограничимся рассмотрением более интересных функций vmalloc и vfree.
38776: Функция vmalloc принимает один параметр — размер области памяти для распределения. Она возвращает указатель распределенной области или NULL, если память нельзя было распределить. Диапазон виртуальных адресов, в пределах которых функция vmalloc может распределять память, ограничена константами VMALLOC_START (строка 11081) и VMALLOC_END (строка 11084). VMALLOC_START начинается на 8 Мб выше конца физической памяти для перехвата любых ошибочных операций доступа к памяти ядра в промежуточной области, a VMALLOC_END располагается немного ниже максимально возможного 32-разрядного адреса в 4 Гб. Если в вашей системе намного больше физической память, чем в системе автора, это значит, что для функции vmalloc потенциально доступно почти все адресное пространство процессора.
38781: Функция vmalloc начинает свою работу с округления затребованного размера в большую сторону до границы следующей страницы, если он еще не выровнен по границе страницы. (Макрокоманда PAGE_ALIGN определена в строке 10842.) Запрос отвергается, если результирующий размер либо слишком мал (0), либо, безусловно, слишком велик.
38784: Попытка найти участок, достаточно большой для размещения блока с размером size с помощью функции get_vm_area, которая рассматривается ниже.
38788: Проверка того, что может быть установлено отображение таблицы страниц с помощью вызова функции vmalloc_area_pages (строка 38701).
38792: Возвращает распределенную область.
38727: Функция get_vm_area пытается возвратить свободный участок в диапазоне от VMALLOC_START до VMALLOC_END. Обычно она выполняет это от имени vmalloc; эта функция используется также в нескольких других случаях, которые здесь не рассматриваются. Вызывающая функция отвечает за обеспечение того, что параметр size является ненулевым кратным размера страницы. Функция vmalloc работает по так называемому алгоритму «первого подходящего», поскольку она возвращает указатель на первый же найденный блок, который удовлетворяет запросу. Существуют также алгоритмы «наиболее подходящего», которые распределяют память из наименьшей доступной свободной области, которая достаточно велика для выполнения запроса, и алгоритмы «наихудшего подходящего», которые всегда распределяют память из наибольшей доступной свободной области. Распределитель памяти каждого типа имеет свои преимущества и недостатки, но реализованный здесь алгоритм «первого подходящего» является простым и быстрым и вполне соответствует своему назначению.
38732: Распределение объекта struct vm_struct для представления новой области. Распределяемые области отслеживаются с помощью отсортированного связанного списка vmlist (строка 38578) объектов 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
38737: Начинается циклический просмотр списка. Цикл должен либо найти достаточно большую свободную область, либо показать, что такой области не существует. В нем вначале проверяется VMALLOC_START, а затем адрес, непосредственно следующий за каждой распределенной областью.
38746: Список был пуст или в цикле была обнаружена достаточно большая область для нового блока; так или иначе, addr теперь представляет собой наименьший доступный адрес. Заполняется новый объект struct vm_struct, который и будет возвращен.
38747: Добавление страницы (размером 4 Кб в архитектуре х86) к размеру зарезервированного блока для перехвата выхода за пределы ядра в направлении старших адресов памяти и, возможно, выхода в направлении младших адресов памяти из следующего по порядку блока. Поскольку это дополнительное пространство не учтено при определении того, было ли достаточно велико текущее отверстие (строка 38738), область, зарезервированная этим входом, может перекрывать следующую, и выход за пределы памяти ядра в эту «лишнюю» область в направлении старших адресов может в действительности перезаписать распределенную память. Правильно? Неправильно. Легко доказать, что значение addr всегда выровнено по странице, и мы уже знаем, что параметр size всегда кратен размеру страницы. Поэтому, если величина size + addr меньше начального адреса следующей области, она должна быть меньше, по крайней мере, на целую страницу. Безусловно, выход за пределы памяти более чем на одну страницу может затронуть следующую область, но выход за пределы памяти менее чем на одну страницу — нет. Поскольку ядро не устанавливает отображение страницы для этой дополнительной памяти, ошибочная попытка обратиться к ней вызовет неразрешимую ситуацию отсутствия страницы (что является почти неслыханным в современных версиях Linux!). В результате ядро перейдет в состояние жесткого останова, но это лучше, чем позволить ядру молча крушить его собственные структуры данных. По крайней мере, вы сразу же узнаете о тяжелом останове, что позволит вам диагностировать проблему; последний же сценарий не станет очевидным до тех пор, пока ядро не разгромит все внешние устройства.
38753: Функция vfree гораздо проще по сравнению с vmalloc (по крайней мере, если функция vmalloc рассматривается в сочетании с get_vm_area), но ради полноты вкратце рассмотрим vfree. Безусловно, здесь addr обозначает начало области vmallocd, которая подлежит освобождению.
38763: Вслед за несколькими простыми проверками допустимости функция выполняет в цикле просмотр vmlist, отыскивая область для освобождения. Этот линейный просмотр заставляет думать, что было бы интересно посмотреть, позволит ли структура сбалансированного дерева, наподобие деревьев AVL, используемая для управления областями VMA, повысить производительность функций vmalloc и vfree.
38764: Если будет найден объект struct vm_struct, соответствующий параметру addr, функция vfree удалит его из списка, освободит структуру и связанные с ней страницы, затем вернет управление. В каждом объекте struct vm_struct хранятся сведения не только о его начальном адресе, но также о его размере; это было удобно для функции get_vm_area и это также удобно здесь, поскольку позволяет функции vfree знать, сколько памяти нужно освободить.
38772: Если бы функция vfree нашла соответствие в списке, она бы уже вернула управление, поэтому соответствие не было найдено. Возможно это плохо, но не настолько плохо, чтобы впадать в панику. Поэтому vfree довольствуется выводом предупредительного сообщения.
netlib.narod.ru | < Назад | Оглавление | Далее > |