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

Struct vm_area_struct


Ядро следит за областями памяти, используемыми процессом, с помощью набора из одного или нескольких объектов struct vm_area_struct, которые принято сокращенно обозначать как VMA. Каждая структура VMA представляет одну сплошную область адресного пространства процесса. Две области VMA никогда не перекрываются— в каждом конкретном процессе адрес может принадлежать не более, чем к одной области VMA; адрес, к которому процесс не обращался никоим образом, не принадлежит ни к одной области VMA.

Одну область VMA отличают от другой следующие две особенности:

  • Две соседние области VMA могут быть несмежными. Это значит, что конец одной области не обязан быть началом другой.
  • Две области VMA могут иметь разные признаки защиты. Например, в одной из них запись может быть разрешена, а в другой — запрещена. Даже если две такие области VMA являются смежными, управление ими должно осуществляться отдельно, поскольку для них могут быть предусмотрены разные признаки защиты.
  • Отметим следующий важный факт: адрес может быть уже охвачен областью VMA, даже несмотря на то, что ядро еще не выделило страницу для его хранения. И действительно, одно из основных назначений областей VMA состоит в поддержке принятия решения о том, как реагировать, когда возникает ситуация отсутствия страницы. Можно считать VMA абстрактным описанием областей памяти, о которых известно процессу, и способов защиты этих областей. Ядро могло бы повторно вычислять большую часть информации, содержащейся в структурах VMA, из таблиц страниц, но это происходило бы гораздо медленнее.

    Структуры VMA процесса хранятся в виде отсортированного списка с двойными связями, в котором для управления списком используются указатели в самих структурах VMA. Когда процесс приобретает такое число структур VMA, которое превышает величину AVL_MIN_MAP_COUNT (установлена директивой #define равной 32 в строке ), ядро создает также для их хранения дерево AVL, опять-таки с применением указателей в самих структурах VMA для управления этим деревом. Дерево AVL — это информационная структура в виде уравновешенного двоичного дерева, поэтому такой подход обеспечивает значительное повышение эффективности поиска при увеличении числа областей VMA. Однако даже после построения дерева AVL поддерживается и линейный список, чтобы ядро могло последовательно просматривать все структуры VMA процесса легко и без рекурсии.


    Двумя наиболее важными элементами объекта struct vm_area_struct являются его члены vm_start и vm_end (соответственно, строки и ), которые определяют начало и конец диапазона адресов, охваченных областью VMA: vm_start — это наименьший адрес в области VMA, a vm_end — на единицу больше наибольшего адреса. В данной главе эти члены будут упоминаться неоднократно.

    Обратите внимание, что vm_start и vm_end имеют тип unsigned long, а не void *, как можно было бы ожидать. Отметим, что в ядре тип данных unsigned long применяется вместо void * во всех местах, где должны быть представлены адреса. Это отчасти предусмотрено для предотвращения появления предупреждающих сообщений компилятора при проведении некоторых вычислений, которые ядро должно выполнять над этими адресами (типа поразрядных операций), и, возможно, также отчасти для предотвращения случайной разадресации с их использованием. При обращении к адресу структуры данных, существующей в пространстве ядра, код ядра использует переменную с указателем. При манипуляции адресами в пространстве пользователя он часто использует unsigned long, и действительно, в коде, рассматриваемом в данной главе, применяется почти исключительно только этот тип данных.

    Отметим, что это накладывает определенные ограничения на компилятор, который используется для компиляции ядра. Применение в качестве адресов переменных с типом данных unsigned long означает, что компилятор должен использовать для unsigned long тип данных такого же размера, как для void *. Однако на практике эти ограничения не столь существенны. При использовании компилятора gcc на компьютере х86 оба эти типа, естественно, имеют размер 32 бита. В архитектурах с 64-разрядными указателями, таких, как Alpha, тип данных unsigned long компилятора gcc также обычно имеет размер 64 бита. Тем не менее, в будущем в результате переноса компилятора gcc на другую архитектуру тип данных unsigned long может приобрести в нем размер, отличный от void *, и эту возможность должны всегда учитывать те, кто занимаются переносом ядра на другую архитектуру.

    Кстати отметим, что изучая возможность применения компиляторов, отличных от gcc, нужно учитывать, что в самом коде ядра уже присутствует значительная часть других особенностей, свойственных gcc. Вполне можно себе представить, что при попытке скомпилировать ядро с использованием какого-то другого компилятора к многочисленному списку полученных ошибок добавится множество сообщений, связанных с различием относительных размеров типов данных unsigned long и void *.


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