---
description: 'Система управления виртуальной памятью во FreeBSD'
next: books/arch-handbook/smp
params:
  path: /books/arch-handbook/vm/
prev: books/arch-handbook/mac
showBookMenu: 'true'
tags: ["Virtual memory", "vm_page_t", "vm_object_t", "I/O", "KVM"]
title: 'Глава 7. Система управления виртуальной памятью'
weight: 8
---

[[vm]]
= Система управления виртуальной памятью
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumoffset: 7
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/arch-handbook/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[vm-physmem]]
== Управление физической памятью `vm_page_t`

Физическая память управляется постранично с использованием структуры `vm_page_t`. Страницы физической памяти классифицируются посредством размещения соответствующих структур `vm_page_t` в одной из нескольких очередей подкачки.

Страница может находиться в одном из следующих состояний: зафиксированном (Wired), активном (Active), неактивном (Inactive), кэшированном (Cache) или свободном (Free). За исключением зафиксированного состояния, страница обычно помещается в двусвязный список, представляющий её текущее состояние. Зафиксированные страницы не помещаются ни в одну из очередей.

FreeBSD реализует более сложную очередь подкачки для кэшированных и свободных страниц с целью реализации раскраски страниц. Каждое из этих состояний включает несколько очередей, организованных в соответствии с размерами кэшей L1 и L2 процессора. Когда требуется выделить новую страницу, FreeBSD пытается получить такую, которая будет достаточно хорошо выровнена с точки зрения кэшей L1 и L2 относительно объекта виртуальной памяти, для которого выделяется страница.

Кроме того, страница может удерживаться с помощью счётчика ссылок или быть заблокированной с помощью счётчика занятости. Система виртуальной памяти также реализует состояние «абсолютной блокировки» страницы с использованием бита PG_BUSY в флагах страницы.

В общем случае каждая из очередей подкачки работает по принципу LRU (наименее недавно использованная). Обычно страница изначально помещается в зафиксированное или активное состояние. В зафиксированном состоянии страница, как правило, ассоциирована с какой-либо таблицей страниц. Система виртуальной памяти «состаривает» страницу, просматривая страницы в более активной очереди страниц (LRU), чтобы переместить их в менее активную очередь. Страницы, перемещённые в кэш, всё ещё связаны с объектом виртуальной памяти, но могут быть немедленно повторно использованы. Страницы в очереди свободных действительно являются полностью свободными. FreeBSD стремится минимизировать количество страниц в очереди свободных, но определённый минимальный объём таких страниц должен поддерживаться для обеспечения возможности выделения памяти во время обработки прерываний.

Если процесс пытается обратиться к странице, которой нет в его таблице страниц, но которая присутствует в одной из очередей подкачки (например, в неактивной или кэшированной), происходит относительно недорогой сбой повторной активации страницы, в результате которого страница повторно активируется. Если же страницы вообще нет в памяти системы, процесс вынужден блокироваться до тех пор, пока страница не будет загружена с диска.

FreeBSD динамически настраивает свои очереди страниц и старается поддерживать разумные пропорции количества страниц в различных очередях, а также сбалансированное соотношение чистых и грязных страниц. Объём перераспределения зависит от текущей нагрузки на память системы. Это перераспределение выполняется демоном выгрузки страниц (pageout daemon) и включает в себя очистку (laundering) грязных страниц (синхронизацию их с резервным хранилищем), отслеживание активности страниц по ссылкам на них (сброс их положения в очередях LRU или перемещение между очередями), миграцию страниц между очередями при разбалансировке очередей и другие действия. Система виртуальной памяти FreeBSD допускает определённое количество сбоев повторной активации страниц, чтобы точнее определить, насколько страница фактически активна или неактивна. Это позволяет принимать более обоснованные решения о том, когда очищать или выгружать страницу в файл подкачки.

[[vm-cache]]
== Унифицированный буферный кэш `vm_object_t`

FreeBSD реализует концепцию универсального «объекта виртуальной памяти» (VM-объекта). VM-объекты могут быть связаны с резервным хранилищем различных типов: без резервного хранилища, с подкачкой, с физическим устройством или с файловым хранилищем. Поскольку файловая система использует те же VM-объекты для управления данными в оперативной памяти, связанными с файлами, в результате получается унифицированный буферный кэш.

Объекты виртуальной памяти могут быть _затемнены_ (shadowed), то есть размещены друг над другом в виде стека. Например, объект виртуальной памяти с подкачкой может быть размещён поверх объекта, связанного с файловым хранилищем, для реализации отображения mmap() с флагом MAP_PRIVATE. Такое стековое размещение также используется для реализации различных общих свойств, включая копирование при записи (copy-on-write), для адресных пространств созданных при форке процесса.

Следует отметить, что структура `vm_page_t` может быть связана только с одним объектом виртуальной памяти (VM-объектом) в данный момент времени. Механизм затемнения VM-объектов реализует кажущееся совместное использование одной и той же страницы между несколькими экземплярами.

[[vm-fileio]]
== Ввод-вывод файловой системы `struct buf`

VM-объекты, поддерживаемые vnode, такие как объекты, связанные с файловым хранилищем, обычно должны поддерживать собственную информацию о чистоте/грязности, независимую от представления о чистоте/грязности в системе виртуальной памяти. Например, когда система виртуальной памяти решает синхронизировать физическую страницу с её резервным хранилищем, система виртуальной памяти должна отметить страницу как чистую до того, как она будет фактически записана в резервное хранилище. Кроме того, файловые системы должны иметь возможность отображать части файла или метаданных файла в KVM для выполнения операций с ними.

Сущности, используемые для управления этим процессом, известны как файловые буферы, структуры ``struct buf`` или ``bp``. Когда файловая система должна работать с частью объекта виртуальной памяти, она обычно отображает часть объекта в структуру struct buf, а затем отображает страницы из этой структуры в KVM. Точно так же операции с дисковым вводом-выводом обычно выполняются путём отображения частей объектов в структуры буферов и последующего выполнения ввода-вывода на этих структурах. Соответствующие им страницы vm_page_t обычно блокируются на время выполнения операции ввода-вывода. Файловые буферы также имеют собственное представление о состоянии занятости, что полезно для кода драйвера файловой системы, который предпочитает работать с файловыми буферами, а не с обычными страницами виртуальной памяти.

FreeBSD резервирует ограниченный объём виртуальной памяти ядра для хранения отображений из структур struct buf на физические страницы памяти, но следует подчеркнуть, что эта память используется исключительно для хранения отображений и не ограничивает возможность кэширования данных. Кэширование физических данных строго связано с ``vm_page_t``, а не с файловыми буферами. Однако, поскольку файловые буферы используются для операций ввода-вывода, они ограничивают количество возможных параллельных операций ввода-вывода. Тем не менее, поскольку обычно доступно несколько тысяч файловых буферов, это редко становится проблемой.

[[vm-pagetables]]
== Отображение таблиц страниц `vm_map_t, vm_entry_t`

FreeBSD разделяет топологию физических таблиц страниц от системы виртуальной памяти. Все основные таблицы страниц, связанные с каждым процессом, могут быть восстановлены динамически и обычно считаются временными (throwaway). Специальные таблицы страниц, такие как те, которые управляют KVM (виртуальной памятью ядра), обычно предварительно выделяются навсегда. Эти таблицы страниц не являются временными.

FreeBSD связывает части vm_object с диапазонами адресов в виртуальной памяти через структуры `vm_map_t` и `vm_entry_t`. Таблицы страниц напрямую создаются из иерархии `vm_map_t` / `vm_entry_t`/ `vm_object_t`. Напоминаем, что физические страницы непосредственно связаны только с `vm_object`, однако это не совсем так. Структуры ``vm_page_t`` также привязаны к таблицами страниц, с которыми они активно ассоциированы. Одна структура `vm_page_t` может быть привязана к нескольким _pmaps_ (так называют таблицы страниц). Тем не менее, иерархическая ассоциация сохраняется, и все ссылки на одну и ту же страницу в одном объекте ссылаются на один и тот же `vm_page_t`, что, в свою очередь, обеспечивает унификацию буферного кэша.

[[vm-kvm]]
== Отображение виртуальной памяти ядра

FreeBSD использует KVM для хранения различных структур ядра. Самой крупной сущностью, хранящейся в KVM, является кэш файловых буферов. То есть, отображения, связанные с сущностями `struct buf`.

В отличие от Linux, FreeBSD _не_ отображает всю физическую память в KVM. Это означает, что FreeBSD может обрабатывать конфигурации памяти до 4 ГБ на 32-битных платформах. На самом деле, если бы MMU это позволял, FreeBSD теоретически могла бы обрабатывать конфигурации памяти до 8 ТБ на 32-битной платформе. Однако, поскольку большинство 32-битных платформ способны отображать только 4 ГБ оперативной памяти, этот вопрос не имеет практического значения.

KVM управляется через несколько механизмов. Основным механизмом для управления KVM является _аллокатор зон_. Аллокатор зон берет блок KVM и делит его на блоки памяти постоянного размера для выделения структур определённого типа. Вы можете использовать команду `vmstat -m`, чтобы получить обзор текущего использования KVM, разбитого по зонам.

[[vm-tuning]]
== Настройка системы виртуальной памяти во FreeBSD

Были преложены значительные усилия сделать ядро FreeBSD динамически настраиваемым. Обычно вам не нужно вмешиваться в настройки, за исключением параметров конфигурации ядра `maxusers` и `NMBCLUSTERS`. Параметры компиляции ядра указанны (обычно) в файле [.filename]#/usr/src/sys/i386/conf/CONFIG_FILE#. Описание всех доступных параметров конфигурации ядра можно найти в файле [.filename]#/usr/src/sys/i386/conf/LINT#.

В большой системе конфигурации, возможно, вам потребуется увеличить значение `maxusers`. Обычно значения варьируются от 10 до 128. Обратите внимание, что слишком большое значение для `maxusers` может привести к переполнению доступной KVM, что вызовет непредсказуемую работу системы. Лучше оставить `maxusers` на разумном уровне и добавлять другие параметры, такие как `NMBCLUSTERS`, для увеличения конкретных ресурсов.

Если ваша система будет активно использовать сеть, возможно, вам захочется увеличить значение `NMBCLUSTERS`. Типичные значения варьируются от 1024 до 4096.

Параметр `NBUF` также традиционно используется для масштабирования системы. Этот параметр определяет объём виртуальной памяти ядра, который система может использовать для отображения файловых буферов для ввода-вывода. Обратите внимание, что этот параметр никак не связан с унифицированным буферным кэшем! В ядрах 3.0-CURRENT и позднее этот параметр динамически настраивается, и в целом не следует настраивать его вручную. Мы рекомендуем вам _не_ пытаться задавать параметр NBUF. Позвольте системе выбрать его автоматически. Слишком малое значение может привести к крайне неэффективной работе файловой системы, в то время как слишком большое значение может привести к истощению очередей страниц из-за того, что слишком много страниц будет закреплено в памяти.

По умолчанию ядра FreeBSD не оптимизированы. Вы можете установить флаги отладки и оптимизации с помощью директивы `makeoptions` в конфигурации ядра. Обратите внимание, что не следует использовать флаг `-g`, если вы не можете разместить в системе большие ядра (обычно более 7 МБ), которые получаются в результате.

[.programlisting]
....
makeoptions      DEBUG="-g" 
makeoptions      COPTFLAGS="-O -pipe"
....

Sysctl предоставляет способ настройки параметров ядра во время работы системы. Обычно вам не нужно изменять какие-либо переменные sysctl, особенно те, что связаны с виртуальной памятью.

Настройка виртуальной памяти (VM) и системы во время работы относительно проста. Во-первых, используйте Soft Updates на ваших файловых системах UFS/FFS, когда это возможно. Файл [.filename]#/usr/src/sys/ufs/ffs/README.softupdates# содержит инструкции (и ограничения) по конфигурации этой функции.

Во-вторых, настройте достаточный объём подкачки (swap). Вы должны создать раздел подкачки на каждом физическом диске, до четырёх, даже на "рабочих" дисках. Объём подкачки должен быть как минимум в 2 раза больше объёма основной памяти, а возможно — и больше, если у вас немного ОЗУ. Размер раздела подкачки следует выбирать с учётом максимальной конфигурации памяти, которую вы когда-либо планируете установить на эту машину, чтобы в будущем не пришлось переразбивать диски. Если вы хотите иметь возможность сохранять дампы при сбое, ваш первый swap-раздел должен быть не меньше объёма основной памяти, а каталог [.filename]#/var/crash# должен иметь достаточно свободного места для хранения дампа.

Подкачка на базе NFS вполне допустима в системах версии 4.X и новее, однако необходимо учитывать, что основная нагрузка на подкачку ляжет на NFS-сервер.
