netlib.narod.ru | < Назад | Оглавление | Далее > |
Функция mmap — это важный системный вызов, который позволяет зарезервировать для различных целей произвольные области памяти. Память может быть отведена для файла или какого-то специального объекта и в этом случае ядро будет поддерживать согласование между областью памяти и объектом, лежащим в ее основе, или это может быть просто старая добрая оперативная память, которая нужна для приложения. (Однако в приложениях mmap обычно не используется просто для распределения памяти, поскольку для этой цели лучше приспособлена malloc.)
Одним из наиболее распространенных способов использования mmap является выполняемое самим ядром отображение в память исполняемого файла (см. в качестве примера строку 8323). Именно так двоичные обработчики работают с механизмом страничного обмена для поддержки исполняемых модулей со страничным обменом по требованию, как упоминалось ранее в этой главе. Выполняемая программа отображается с помощью mmap в соответствующую область пространства памяти процесса и функция do_page_fault переносит в память остальные страницы выполняемой программы по мере необходимости.
Память, заполненная с помощью функции mmap, может быть отмечена как предназначенная для выполнения, заполнена командами, а затем в нее может быть выполнен переход; именно так работают JIT-компиляторы Java. Еще проще отобразить выполняемые файлы прямо в пространство памяти процесса, во время его выполнения; этот метод используется для реализации библиотек с динамическим связыванием.
Функцией ядра, которая реализует функциональные возможности mmap является do_mmap.
33240: Функция do_mmap принимает несколько параметров; вместе они определяют файл или устройство, которое должно быть отображено в память, если оно указано, а также предпочтительный адрес и характеристики области памяти, которая должна быть создана.
33252: Величина TASK_SIZE составляет то же, что и PAGE_OFFSET, которая определена в строке 10867; она равна 0хс0000000 или 3 Гб. Это максимальный объем памяти, который может занимать любой пользовательский процесс, в чем и состоит назначение этого кода: очевидно, что если от функции do_mmap потребуют распределить более 3 Гб или в пространстве адресов 3 Гб вслед за addr не осталось достаточно места, запрос должен быть отвергнут.
33275: Если параметр file имеет значение NULL, это значит, что функция do_mmap получила запрос выполнить анонимное отображение, т.е. отображение, которое фактически не подключено к какому-либо файлу или к другому специальному объекту. В ином случае отображение связано с файлом и do_mmap должна перейти к проверке того, что флажки, которые предполагается установить для этой области памяти, совместимы с операциями, которые пользователю разрешено выполнять с этим файлом. Например, в строке 33278 функция проверяет, что если область памяти предназначена для записи, файл был открыт для записи. Пропуск этих проверок создавал бы возможность игнорировать проверки, которые были выполнены при открытии файла.
33307: Вызывающей программе разрешено настаивать на том, чтобы функция do_mmap либо предоставила отображение по затребованному адресу, либо вообще его не предоставила. В таком случае функция do_mmap просто проверяет, что указанный адрес начинается на границе страницы. Иначе она получает первый доступный адрес до или после addr (с помощью вызова функции get_unmapped_area, которая начинается в строке 33432) и использует его.
33323: Создает область VMA и начинает ее заполнять.
33333: Если на обработку функции mmap поступает файл, предназначенный для чтения, область памяти будет отмечена как предназначенная для чтения, записи и выполнения. (Функция do_mmap может вскоре отозвать разрешение на запись — это сделано только для упрощения.) К тому же, если функция получает запрос сделать область памяти разделяемой, она это делает.
33347: Если файл не предназначен для записи, область памяти также не должна быть предназначена для записи.
33351: В этом случае нет файла, с режимами открытия и разрешениями которого должна быть совместима функция do_mmap, то есть ей разрешено действовать по своему усмотрению. Поэтому она отмечает область памяти как предназначенную для чтения, записи и выполнения.
33361: Очистка всех старых отображений памяти в устанавливаемом диапазоне адресов с использованием do_munmap (вскоре она будет рассмотрена). Поскольку новая область VMA еще не была вставлена в список процесса (о ней сейчас знает только функция do_mmap), на новую область VMA этот вызов не распространяется.
33406: Больше ничего плохого не может случиться. Функция do_mmap вставляет область VMA в список областей VMA процесса (и, возможно, в его дерево AVL), сливает все сегменты, ставшие теперь смежными (функция merge_segments рассматривается ниже), обновляет некоторую статистическую информацию и возвращает адрес нового отображения.
33892: merge_segments — это интересная функция, которая сливает смежные области VMA в одну область VMA с большим охватом. Таким образом, если одна область VMA покрывает (вполне очевидно, что вымышленный) диапазон от 0x100 до 0x200, а другая покрывает диапазон от 0x200 до 0x300 и они имеют одинаковые признаки защиты, то функция merge_segments заменяет их обе одной областью VMA, которая покрывает диапазон от 0x100 до 0x300. (Отметим, что слово «segments» в имени функции не говорит о том, что мы используем сегменты процессора — это не так.)
Параметрами функции merge_segments являются: объект struct mm_struct, содержащий интересующие нас области VMA, а также начальный и конечный адреса, в пределах которых может оказаться возможным слияние.
33897: find_vma_prev (строка 33501) находит первую область VMA, для которой значение vm_end следует за предоставленным значением start_addr, поэтому это первая область VMA, которая может содержать start_addr. Напомним также, что функция find_vma_prev возвращает также указатель на предыдущую область VMA в параметре prev1 (NULL, если запросу удовлетворяет первая же область VMA).
33911: Входит в цикл, в котором будут рассмотрены все области VMA, перекрывающие заданный диапазон. В этом цикле функция merge_segments пытается слить каждый сегмент с предшествующим, который всегда можно определить с помощью prev.
33921: Основная часть этого условия if относительно проста, но последняя проверка, вероятно, сложнее. Она проверяет смежность prev и mpnt, т.е. отсутствие неотображенной памяти между концом prev и началом mpnt. Даже если она обнаруживает, что vm_end одной области равен vm_start другой, это пока не значит, что эти две области являются смежными — помните, что vm_end на единицу больше последнего адреса, принадлежащего VMA. Строки с 33926 до 33932 предписывают аналогичное свойство для файлов и разделяемой памяти, обработанных функцией mmap: конец одной области должен быть равен началу следующей.
33937: Функция merge_segments нашла смежные области VMA, которые она может слить. Она удаляет mpnt из списка областей VMA (и возможно также из дерева) и вкладывает это значение в prev. Отметим, что эта функция не демонтирует дерево, даже если число областей VMA снова становится меньше значения AVL_MIN_MAP_COUNT.
33948: Если исчезнувшая область VMA была частью файла, обработанного функцией mmap, функция merge_segments удаляет ее ссылку на этот файл.
33689: Очевидно, что функция do_munmap противоположна do_mmap; она удаляет отображения виртуальной памяти из пространства памяти процесса.
33695: Если адрес, который должен быть изъят из отображения с помощью функции do_munmap, не выровнен по границе страницы или соответствующая ему область памяти лежит вне пространства памяти процесса, ясно, что он недействителен, поэтому такая попытка будет здесь отвергнута.
33699: Если не была освобождена, по крайней мере, одна страница, попытка отвергается.
33707: Поиск области VMA, которая включает указанный адрес. Любопытно, что функция do_munmap возвращает 0 — признак отсутствия ошибки — если адрес не находился ни в одной области VMA. В определенном смысле, это правильно; функция do_rnunmap получила запрос проверить то, что процесс больше не имеет отображения для данной области памяти и это легко сделать, если такого отображения не было с самого начала. Но это все равно вызывает удивление; может оказаться, что со стороны вызывающей функции допущена ошибка и что функция do_munmap должна поэтому как-то об этом сообщить. Однако некоторые вызывающие функции основаны на том, что так и должно быть на самом деле, например, см. строку 33361.
33717: Если указанный диапазон памяти лежит полностью внутри отдельной области VMA, но не соприкасается ни с одним концом содержащей его области VMA, то удаление этого участка привело бы к возникновению отверстия во включающей его области VMA. Ядро не может допустить возникновения отверстия, поскольку области VMA по определению представляют собой непрерывные области памяти. Поэтому в таком случае функция do_munmap должна создать другие области VMA, по одной с каждой стороны отверстия. Однако, если ядро уже создало такое количество областей VMA, какое разрешено для данного процесса, оно не может это выполнить, поэтому функция do_munmap не в состоянии удовлетворить запрос.
33730: Поиск всех областей VMA, перекрывающих или лежащих внутри участка, предназначенного для освобождения, и размещение каждой из этих областей в локальном стеке областей VMA, предназначенных для освобождения. В этом процессе функция do_munmap удаляет области VMA из содержащего их дерева AVL, если оно имеется.
33743: Функция do_munmap построила стек областей VMA, предназначенных для освобождения; теперь она может их освободить.
33748: Вычисление точного размера диапазона адресов, предназначенного для освобождения, с учетом того, что он может не охватывать всю эту область VMA. Предусмотрев подходящие определения для min и max, эти три строки можно записать следующим образом:
st = max(mpnt->vm_start, addr); end = min(mpnt->vm_end, addr + len);
Поэтому st — это начало области, с которого функция do_munmap фактически начнет освобождение, а end — конец этой области.
33756: Если эта область VMA составляла часть разделяемого отображения, функция do_munmap удаляет mpnt из списка разделяемых областей VMA с помощью вызова функции remove_shared_vm_struct (строка 33140).
33759: Обновляет структуры данных MMU, соответствующие текущей подобласти, которая была освобождена внутри данной области VMA.
33765: Исправляет отображение с помощью вызова функции unmap_fixup, которая будет рассмотрена ниже.
33773: Функция do_munmap освободила все отображения, представленные областями VMA в этом диапазоне; последний важный шаг состоит в освобождении таблиц страниц одного и того же диапазона, что было выполнено с помощью вызова функции free_pgtables (строка 33645).
33578: Функция unmap_fixup исправляет переданное ей отображение областей VMA путем корректировки одного из концов, создания отверстия в середине или полного удаления области VMA.
33590: Первый из этих случаев прост: отмена отображения всей области. Функция unmap_fixup просто должна закрыть основополагающий файл или другой объект, если он имеется. Можно видеть, что это не приводит к удалению самой области VMA из current->mm; она уже была удалена вызывающей функцией. Поскольку для всего диапазона области VMA отменено отображение, нечего откладывать, поэтому функция unmap_fixup просто возвращает управление.
33599: Следующие два случая касаются удаления диапазона с начала или с конца VMA. Они также довольно просты; в основном они должны скорректировать члены vm_start или vm_end объекта VMA.
33608: Это самый интересный из всех четырех случаев — удаление участка в середине VMA и, тем самым, создание отверстия. Функция начинает свою работу с создания локальной копии переданной ей дополнительной области VMA, а затем устанавливает параметр *extra в NULL в качестве указания для вызывающей функции, что была использована дополнительная область VMA.
33611: Процесс разбиения VMA показан на рис. 8.4. Большая часть информации просто копируется из старой области VMA в новую, после чего функция unmap_fixup корректирует диапазоны обоих областей VMA с учетом отверстия. Первоначальная область VMA, area, сокращается, с тем, чтобы представить подобласть перед новым отверстием, a mpnt становится подобластью, расположенной после него.
Рис. 8.4. Разделение VMA
33626: Вставляет полностью новую подобласть в current->mm.
33629: Во всех случаях, кроме первого, функция unmap_fixup сохраняет старую область VMA. Она стала меньше, но не пуста, поэтому она снова будет вставлена в набор current->mm областей VMA.
netlib.narod.ru | < Назад | Оглавление | Далее > |