Главная » Ядро Linux » Области памяти

0

 (memory areas)   представляются  с  помощью  объектов областей памяти,  которые хранятся в  структурах  типа  vm_area_struct .  Эта  структура  определена  в файле  <linux/mm.h> .   часто  называются областями  виртуальной памяти (virtual memory area, или VMA).

Структура vm_area_struc t  описывает  одну  непрерывную  область  памяти в  данном  адресном пространстве. Ядро  рассматривает  каждую  область  памяти, как  уникальный объект.   Для  каждой области памяти  определены некоторые общие   свойства, такие  как  права  доступа  и  набор   соответствующих операций. Таким  образом, одна  структура VMA может  представлять различные типы   областей памяти,  например  файлы, отображаемые в память, или  стек  пространства пользователя. Это  аналогично  объектно-ориентированному  подходу, который используется в подсистеме VFS (см  .главу  12, "Виртуальная файловая  система").

Ниже  показана эта  структура данных  с комментариями, описывающими назначение  каждого поля.

struct vm_area_struct {

struct mm_struct     *vm_mm;  /* соответствующая структура mm_struct */ unsigned long       vm_start;    /* начало диапазона адресов */ unsigned    long    vm_end;      /* конец диапазона адресов */

struct vm_area_struct *vm_next;     /* список областей VMA */ pgprot_t            vm_page_prot; /* права доступа */ unsigned long       vm_flags;    /* флаги */

struct rb_node      vm_rb;       /* узел текущей области VMA */

union { /* связь с address_space->i_mmap, или i_mmap_nonlinear */

struct {

struct list_head      list; void                 *parent; struct vm_area_struct  *head;

} vm_set;

struct prio_tree_node prio_tree_node;

} shared;

struct list_head    anon_vma_node; /* анонимные области */ struct anon_vma       *anon_vma;    /* объект анонимной VMA */ struct vm_operations_struct   *vm_ops; /* операции */

unsigned long       vm_pgoff;    /* смещение в файле */

struct file         *vm_file;    /* отображенный файл (если есть) */

void                *vm_private_data; /* приватные данные */

};

Как  уже было  рассказано, каждый  дескриптор памяти связан с уникальным диапазоном (интервалом) адресов  в адресном пространстве процесса. Поле  vm_start — это  начальный (минимальный) адрес, а поле  vm_end—  конечный (максимальный) адрес  данного интервала. Следовательно, значение (vm_end  vm_start )  — это размер  (длина) интервала адресов  в байтах.  Интервалы адресов  разных  областей  памяти  одного  адресного пространства не могут перекрываться.

Поле  vm_mm  указывает па  структуру  mm_struct, связанную с данной областью VMA.  Заметим, что каждая  область  VMA уникальна для  той  структуры  mm_struct, с которой эта область  связана. Поэтому, даже если  два разных  процесса отображают один  и тот же файл  на  свои  адресные пространства,  то для  каждого  процесса создается своя  структура  vm_area_struct , чтобы  идентифицировать уникальные области  памяти каждого  процесса. Следовательно, два потока, которые совместно используют адресное  пространство, также  совместно используют и  все  структуры vm_area_struct в этом  адресном пространстве.

Флаги областей VMA

Поле  флагов  vm_flags  содержит битовые  флаги,  которые определены в файле

<linux/mm.h>. Они  указывают особенности поведения и содержат  описательную информацию о страницах памяти, которые входят  в данную  область  памяти. В отличие  от прав доступа, которые связаны с определенной физической страницей памяти, флаги  областей  VMA указывают особенности поведения, за которые отвечает ядро, а не аппаратное обеспечение. Более  того, поле  vm_flags содержит информацию, которая относится к каждой  странице в области  памяти или, .что то же самое, ко всей области  памяти в целом.  В табл. 14.1 приведен список возможных значений флагов vm_flags.

Таблица 14.1 . Флаги областей VMA

Флаг                              Влияние на область УМА и на ее страницы памяти

VM_READ VM_WRITE VM_EXEC VM_SHARED VM_MAYREAD VM_MAYWRITE VM_MAYEXEC VM_MAYSHARE VM_GROWSDOWN VM_GROWSUP VM_SHM VM_DENYWRITE VM_EXECUTABLE VM_LOCKED VM_IQ

VM_SEQ_READ VM_RAND_READ VM_DONTCOPY VM_DONTEXPAND VM_RESERVED VM_ACCOUNT VM_HUGETLB VM_NONLINEAR

Из страниц памяти можно считывать информацию

В страницы памяти можно записывать информацию Можно выполнять код, хранящийся в страницах памяти Страницы памяти являются совместно используемыми Можно устанавливать флаг VM_REA D

Можно устанавливать флаг VM_WRIT E Можно устанавливать флаг VM_EXE C Можно устанавливать флаг VM_SHARE D

Область памяти может расширяться "вниз"

Область памяти может расширяться "вверх"

Область используется для разделяемой (совместно используемой) памяти

В область отображается файл, в который нельзя выполнять запись

В область отображается выполняемый файл

Страницы памяти в области являются заблокированными

В область памяти отображается пространство ввода-вывода аппаратного устройства

К страницам памяти, вероятнее всего, осуществляется последовательный доступ К страницам памяти,  вероятнее всего, осуществляется случайный доступ Область памяти не должна копироваться при  вызове for k ()

Область памяти не может быть увеличена с помощью вызова remap () Область памяти не должна откачиваться на диск

Область памяти является объектом, по которому выполняется учет ресурсов

В области памяти используются гигантские (hugetlb ) страницы памяти

Область памяти содержит нелинейное отображение

Рассмотрим подробнее назначение наиболее интересных и важных  флагов. Флаги VM_READ, VM_WRITE и VM_EXEC указыпают обычные права  на чтение-запись и выполнение  для  страниц  памяти,   которые  принадлежат  данной   области  памяти.   Пр и  необходимости их  можно комбинировать для  формирования  соответствующих прав  доступа.  Например,  отображение выполняемого кода  процесса может  быть  выполнено с указанием флагов VM_READ и VM_EXEC, но  никак не  с указанием флага  VM_WRITE. С  другой  стороны,  сегмент данных из  выполняемого файла может  отображаться с указанием флагов VM_READ и VM_WRITE, указывать при  этом  флаг  VM_EXEC не  имеет смысла. Файл  данных, который отображается только  для  чтения, должен отображаться с указанием только флага  VM_READ.

Флаг  VM_SHARED указывает на  то, что  область  памяти  содержит отображение, которо е  может  совместно использоваться  несколькими  процессами .  Если   этот флаг  установлен, то  такое  отображение называют совместно используемым  (shared mapping),  что  интуитивно понятно. Если  этот  флаг  не  установлен, то  такое  отображение доступно только одному процессу и оно  называется частным  отображением, (private  mapping).

Флаг  VM_IO  указывает,  что  область   памяти  содержит отображение области ввода-вывода  аппаратного  устройства.  Этот  флаг  обычно устанавливается драйверами устройств при  выполнении вызова  mmap  ()   для  отображения в  память области ввода-вывода аппаратного устройства. Кроме  всего  прочего,  этот  флаг  указывает,  что область  памяти не должна  включаться в файл  core  процесса. Флаг  VM_RESERVED указывает, что  область  памяти не должна  откачиваться на  диск.  Этот  флаг  также  укалывается  при  отображении на  память областей ввода-вывода аппаратных устройств.

Флаг  VM_SEQ_READ является подсказкой ядру, что  приложение выполняет последовательное (т.е.  линейное и  непрерывное) чтение  из  соответствующего отображения.  При  этом  ядро  может  повысить производительность чтения за счет выполнения упреждающего чтения  (read-ahead)  из  отображаемого файла.  Флаг  VM_RAND_READ указывает обратное, т.е.  приложение выполняет операции чтения из  случайно выбранных мест  отображения  (т.е.  не  последовательно).  При  этом  ядро  может  уменьшить  или  совсем   отключить выполнение  упреждающего чтения  из  отображаемого файла. Эти  флаги  устанавливаются с помощью системного вызова  madvic e ()   путем указания соответственно флагов  MADV_SEQUENTIAL и MADV_RANDOM для  этого  вызова.  Упреждающее  чтение  — это  последовательное чтение  несколько  большего количества  данных, чем  было  запрошено, в надежде  на то, что дополнительно считанные данные могут  скоро  понадобиться. Такой режим  полезен для  приложений, которые считывают данные последовательно. Однако если  считывание данных  выполняется случайным образом,  то  режим  упреждающего чтения не  эффективен.

Операции с областями VMA

Поле vm_ops структуры vm_area_struc t содержит указатель на таблицу операций, которые связаны с данной областью памяти и которые ядро может вызывать для манипуляций с  областью VMA. Структура vm_area_struc t служит общим объектом для представления всех типов областей виртуальной памяти, а в таблице операций описаны конкретные методы, которые могут быть применены к каждому конкретному экземпляру объекта.

Таблица операций представлена с  помощью структуры vm_operations_struct , которая определена в файле <linux/mm.h> следующим образом.

structvm_operations_struct {

void (*open) (struct vm_area_struct *) ;

void (*close) (struct vm_area_struct *) ;

struct page * (*nopage) (struct vm_area_struct *, unsigned long, int);

int (*populate) (struct vm_area struct *, unsigned long, unsigned long, pgprot_t, unsigned long, int);

};

Рассмотрим каждый метод в отдельности.

• void open (struct vm_area_struct *area)

Эта функция вызывается, когда соответствующая область памяти добавляется в адресное пространство.

• void close(struct vm_area_struct *area)

Эта функция вызывается, когда соответствующая область памяти удаляется из адресного пространства.

• struct page * nopage(struct vm_area_sruct *area, unsigned long address,

int unused)

Эта функция вызывается обработчиком прерывания из-за отсутствия страницы (page fault), когда производится доступ к странице, которая отсутствует в физической памяти.

• int populate {struct vm_area_struct *area, unsigned long address,

unsigned long len, pgprot_t prot,

unsigned long pgoff, int nonblock)

Эта функция вызывается из системного вызова. remap_pages() для предварительного заполнения таблиц страниц области памяти (prefault) при создании нового отображения.

Списки и деревья областей памяти

Как  уже  рассказывалось,  к  областям памяти  осуществляется доступ  с  помощью двух  структур   данных   дескриптора памяти:  полей   mmap   и mm_rb. Эти  две  структуры  данных  независимо друг  от друга указывают на  все  области памяти,  связанные с данным дескриптором памяти.  Они  содержат указатели на  одни  и  те  же  структуры vm_area_struct ,  просто   эти  указатели связаны друг с другом  по-разному.

Первый контейнер, поле  mmap, объединяет все объекты областей памяти в односвязный список. Структуры vm_area_struc t объединяются в список с помощью своих  полей   vm_next .   отсортированы в  порядке увеличения адресов (от  наименьшего и до  наибольшего).  Первой  области памяти  соответствует структура  vm  area_struct ,  на  которую указывает  само  поле  mmap.  Указатель на  самую последнюю структуру  равен  значению NULL.

Второе   поле, mm_rb , объединяет все  объекты  областей памяти  в  красно-черное  (red-black) дерево.   На  корень дерева  указывает поле mm_rb , а  каждая структура vm_area    struc t  присоединяется к дереву  с  помощью поля  vm_rb.

Красно-черное дерево  — это  один  из  типов  бинарного дерева.   Каждый элемент красно-черного дерева  называется узлом.   Начальный узел  является корнем дерева. Большинство узлов  имеет  два  дочерних узла:  левый  дочерний узел  и  правый дочерний  узел.  Некоторые узлы  имеют  всего  один  дочерний узел, и, наконец, узлы, которые  не имеют  дочерних, называются листьями. Для любого  узла все элементы дерева, которые находятся слева  от данного узла, всегда  меньше по своему  значению, чем  значение  данного узла, а все  элементы дерева, которые находятся справа  от некоторого узла, всегда  больше  по  значению, чем  значение этого  узла.  Более  того, каждому  узлу присвоен цвет  (красный или  черный, отсюда  и название этого  типа  деревьев) в соответствии со следующими двумя  правилами: дочерние элементы красного узла являются  черными и любой  путь  по  дереву  от узла  к листьям должен  содержать одинаковое количество черных  узлов.  Корень дерева  всегда  красный. Поиск,  вставка и удаление элементов из такого  дерева  требуют  количество операций порядка О (lo g (n)  ).

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

в реальной жизни

Рассмотрим  пример    адресного    пространства  процесса   и  области  памяти   в  этом адресном   пространстве.  Для   этой   цели   можно   воспользоваться  полезной   файловой системой   /рго с  и  утилитой   pmар  (1) .   В  качестве   примера   рассмотрим  следующую простую   прикладную   программу,   которая   работает  в  пространстве  пользователя.  Эта программа   не  делает  абсолютно   ничего,   кроме  того,   что  служит  примером.

int main(int argc, char *argv[])

return 0;

}

Рассмотрим  список   областей   памяти   из  адресного   пространства  этого   процесса. Этих  областей   немного.   Мы  уже  знаем,   что  среди  них  есть  сегмент   кода,   сегмент данных   сегмент  bss.  Если  учесть,   что  эта  программа   динамически  скомпонована  с библиотекой  функций  языка   С,   то  соответствующие   области   существуют  также   для модуля   libc.s o  и для  модуля   ld.so .  И  наконец, среди  областей памяти также  есть стек   процесса.

Результат  вывода  списка   областей   адресного   пространства  этого  процесса   из  файла   /proc/<pid>/map s  имеет   следующий   вид.

rml@phantasy:~$ cat /proc/1426/maps

00e80000-00faf000 r-xp 00000000 03:01 208530 /lib/tls/libc-2.3.2.so

00faf000-00fb2000 rw-p 0012fOOO 03:01 208530 /lib/tls/libc-2.3.2.so

00fb2000-00fb4000 rw-p 00000000 00:00 0

08048000-08049000 r-xp 00000000 03:03 439029 /home/rml/src/example

08049000-0804a000 rw-p 00000000 03:03 439029 /home/rml/src/example

40000000-40015000 r-xp 00000000 03:01 80276  /lib/ld-2.3.2.so

40015000-40016000 rw-p 00015000 03:01 80276  /lib/ld-2.3.2.so

4001e000-4001f000 rw-p 00000000 00:00 0 bfffe000-c0000000 rwxp fffffOOO 00:00 0

Информация  об  областях  памяти   выдается   в  следующем  формате.

начало-конец     права   доступа     смещение      старший:младший   номера   устройства файловый  индекс     файл

Утилита рmар  (1) 4   форматирует эту  информацию в следующем, более  удобочитаемом   виде.

rml@phantasy:~$ pmap 1426 example[1426]

OOe8OOOO (1212 KB) r-xp (03:01 208530)       /lib/tls/libc-2.3.2.so

OOfafOOO (12 KB)   rw-p (03:01 208530)       /lib/tls/libc-2.3.2.so

00fb2000 (8 KB)   rw-p (00:00 0)

08048000 (4 KB)    r-xp (03:03 439029)       /home/rml/src/example

08049000 (4 KB)    rw-p (03:03 439029)       /home/rml/src/example

40000000 (84 KB)   r-xp (03:01 80276)        /lib/ld-2.3.2.so

4 Утилита  pmap(l )  печатает  форматированны й  список  областей  памяти   процесса.   Результат  ее  вывода несколько   более  удобочитаем,   чем  информация ,  получаемая  из  файлово й  системы  /ргос ,  но это  одна  и  та же  информация .  Данная  утилита  включена  в новые  версии  пакета  procps.

40015000 (4KB)    rw-p (03:01 80276)         /lib/ld-2.3.2.so

4001e000 (4 KB)   rw-p (00:00 0)

bfffeOOO (8 KB)   rwxp (00:00 0)

mapped: 1340 KB   writable/private:  40 KB  shared: 0 KB

Первые три строчки соответствуют сегменту кода,  сегменту данных и сегменту bss  модуля libc.s o  (библиотека функций языка С). Следующие две   строчки описывают соответственно сегмент кода и сегмент данных выполняемого  образа. Далее три строчки— описание сегментов кода, данных и bss  модуля  ld.  so  (динамический компоновщик).  Последняя строчка описывает стек процесса.

Обратите внимание,  что   все   сегменты кода имеют права на чтение и выполнение, что   и должно быть для выполняемых образов. С другой стороны,  сегменты данных и bss, которые содержат глобальные переменные,  помечаются как имеющие права на запись и чтение, а не на выполнение.

Все   адресное пространство составляет порядка  1340   Кбайт,  но только 40  Кбайт из них имеют право на запись и соответствуют частному отображению. Если область памяти является совместно используемой и не имеет прав на запись, то  ядро хранит в памяти всего одну копию  отображаемого файла.  Это может показаться обычным для совместно используемых отображений; однако, случай, когда при этом еще и отсутствуют права на запись,  проявляется  несколько неожиданно.  Если учесть факт, что  когда на отображение нет прав записи, то  соответствующая информация никогда  не может быть изменена (из отображения возможно только чтение), становится ясно, что  можно совершенно безопасно загрузить выполняемый образ в  память всего  один раз. Поэтому динамически загружаемая библиотека функций языка С и занимает в памяти всего 1212  Кбайт, а не 1212  Кбайт, умноженное на количество процессов, которые эту  библиотеку используют. В  связи с  этим, процесс, код и данные которого имеют объем порядка 1340  Кбайт,  на самом деле занимает всего 40  Кбайт физической памяти. Экономия памяти из-за такого совместного использования получается  существенной.

Обратите внимание на области памяти, которые не имеют отображаемого файла, находятся на устройстве с  номерами  00:00 и номер файлового индекса для которых равен нулю. Это отображение страницы,  заполненной  нулями (zero page, пулевая страница). Если отобразить страницу, заполненную нулями,  на область памяти, которая имеет права на запись, то  побочным эффектом является инициализация всех переменных в  нулевые значения.  Это важно, поскольку в  таком случае получается область памяти, заполненная нулями, которая нужна для сегмента bss.

Каждой области памяти,  связанной с  процессом,  соответствует структура vm_ area_struct . Так как процесс не является потоком (thread), то  для него существует отдельная структура min_struct,  на которую есть ссылка из структуры task_struct .

Источник: Лав,  Роберт. Разработка ядра  Linux, 2-е  издание. : Пер.  с англ.  — М.  : ООО  «И.Д.  Вильяме» 2006. — 448 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии