Когда процесс, связанный с определенным адресным пространством, завершается, то вызывается функция exit_mm()
. Эта функция выполняет некоторые служебные действия и обновляет некоторую статистическую информацию. Далее вызывается функция mput()
, которая уменьшает на единицу значение счетчика количества пользователей mm_users
для дескриптора памяти. Когда значение счетчика количества пользователей становится равным нулю, то вызывается функция mmdrop()
, которая уменьшает значение основного счетчика использования mm_count
. Когда и этот счетчик использования наконец достигает нулевого значения, то вызывается функция free_mm()
, которая возвращает экземпляр структуры mm_struct
в слябовый кэш mm_cachep
с помощью вызова функции kmem_cache_free()
, поскольку дескриптор памяти больше не используется.
Структура mm_struct
и потоки пространства ядра
Потоки пространства ядра не имеют своего адресного пространства процесса и, следовательно, связанного с ним дескриптора памяти. Значение поля mm
для потока пространства ядра равно NULL
. Еще одно определение потока ядра — это процесс, который не имеет пользовательского контекста.
Отсутствие адресного пространства— хорошее свойство, поскольку потоки ядра вообще не обращаются к памяти в пространстве пользователя (действительно, к какому адресному пространству им обращаться?). Поскольку потоки ядра не обращаются к страницам памяти в пространстве пользователя, им вообще не нужен дескриптор памяти и таблицы страниц (таблицы страниц обсуждаются дальше в этой главе). Несмотря на это, потокам пространства ядра все же нужны некоторые структуры данных, такие как таблицы страниц, чтобы обращаться к памяти ядра. Чтобы обеспечить потоки ядра всеми данными без необходимости тратить память на дескриптор памяти и таблицы страниц, а также процессорное время на переключение на новое адресное пространство и так далее, каждый поток ядра использует дескриптор памяти задания, которое выполнялось перед ним.
Когда процесс запланирован на выполнение, то загружается адресное пространство, на которое указывает поле mm
этого процесса. Поле active
_mm дескриптора процесса обновляется таким образом, чтобы указывать на новое адресное пространство. Потоки ядра не имеют своего адресного пространства, поэтому значение поля mm для них равно NULL
. Поэтому, когда поток ядра планируется на выполнение, ядро определяет, что значение ноля mm
равно NULL
, и оставляет загруженным предыдущее адресное пространство. После этого ядро обновляет поле active_mm
дескриптора процесса для потока ядра, чтобы он указывал на дескриптор памяти предыдущего процесса. При необходимости поток ядра может использовать таблицы страниц предыдущего процесса. Так как потоки ядра не обращаются к памяти в пространстве пользователя, то они используют только ту информацию об адресном пространстве ядра, которая связана с памятью ядра и является общей для всех процессов.
Области памяти
Области памяти (memory areas) представляются с помощью объектов областей памяти, которые хранятся в структурах типа vm_area_struct
. Эта структура определена в файле <linux/mm.h>
. Области памяти часто называются областями виртуальной памяти (virtual memory area, или VMA).
Структура vm_area_struct
описывает одну непрерывную область памяти в данном адресном пространстве. Ядро рассматривает каждую область памяти, как уникальный объект. Для каждой области памяти определены некоторые общие свойства, такие как права доступа и набор соответствующих операций. Таким образом, одна структура 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
Флаг | Влияние на область VMA и на ее страницы памяти |
---|---|
VM_READ |
Из страниц памяти можно считывать информацию |
VM_WRITE |
В страницы памяти можно записывать информацию |
VM_EXEC |
Можно выполнять код, хранящийся в страницах памяти |
VM_SHARED |
Страницы памяти являются совместно используемыми |
VM_MAYREAD |
Можно устанавливать флаг VM_READ |
VM_MAYWRITE |
Можно устанавливать флаг VM_WRITE |
VM_MAYEXEC |
Можно устанавливать флаг VM_EXEC |
VM_MAYSHARE |
Можно устанавливать флаг VM_SHARED |
VM_GROWSDOWN |
Область памяти может расширяться "вниз" |
VM_GROWSUP |
Область памяти может расширяться "вверх" |
VM_SHM |
Область используется для разделяемой (совместно используемой) памяти |
VM_DENYWRITE |
В область отображается файл, в который нельзя выполнять запись |
VM_EXECUTABLE |
В область отображается выполняемый файл |
VM_LOCKED |
Страницы памяти в области являются заблокированными |
VM_IQ |
В область памяти отображается пространство ввода-вывода аппаратного устройства |
VM_SEQ_READ |
К страницам памяти, вероятнее всего, осуществляется последовательный доступ |
VM_RAND_READ |
К страницам памяти, вероятнее всего, осуществляется случайный доступ |
VM_DONTCOPY |
Область памяти не должна копироваться при вызове fork() |
VM_DONTEXPAND |
Область памяти не может быть увеличена с помощью вызова remap() |
VM_RESERVED |
Область памяти не должна откачиваться на диск |
VM_ACCOUNT |
Область памяти является объектом, по которому выполняется учет ресурсов |
VM_HUGETLB |
В области памяти используются гигантские (hugetlb ) страницы памяти |
VM_NONLINEAR |
Область памяти содержит нелинейное отображение |