Перевод на русский язык: Андрей Захватов (<andy@FreeBSD.org>)
Материал предоставил Matthew Dillon <dillon@FreeBSD.org>. 6 февраля 1999
Физическая память управляется на уровне отдельных страниц посредством структур vm_page_t. Страницы физической памяти разделены на категории через помещение соответствующих им структур vm_page_t в одну из нескольких очередей страниц.
Страница может находиться в связанном, активном, неактивном, кэшированном или свободном состоянии. Кроме случая связанного состояния, страница обычно помещается в двойной связный список очереди, соответствующей состоянию страницы. Закрепленные страницы не помещаются ни в какую очередь.
Для реализации алгоритма подгонки страниц во FreeBSD в основном применяется очередь для кэшированных и свободных страниц. Каждое из этих состояний затрагивает несколько очередей, которые строятся согласно размерам кэшей L1 и L2 процессора. Когда требуется выделение новой страницы, FreeBSD пытается получить ту, что расположена в достаточной мере рядом с точки зрения кэшей L1 и L2 относительно объекта VM, для которого выделяется страница.
Кроме того, страница может удерживаться счетчиком ссылок или может быть заблокирована счетчиком занятости. Система VM также реализует состояние "безусловной блокировки" для страницы установкой бита PG_BUSY в поле флагов страницы.
Говоря общими словами, каждая из очередей страниц работает по принципу LRU. Первоначально страница обычно приводится в связанное или активное состояние. Если она связана, то страница обычно указана где-то в таблице страниц. Система VM отслеживает устаревание страницы, сканируя страницы в более активной очереди страниц (LRU) для того, чтобы переместить их в менее активную очередь страниц. Страницы, которые перемещены в кэш, остаются связанными с объектом VM, но являются кандидатами на немедленное повторное использование. Страницы в очереди свободных страниц действительно являются свободными. FreeBSD пытается минимизировать количество страниц в очереди свободных страниц, однако для обеспечения выделения страниц во время обработки прерываний должно поддерживаться некоторое минимальное количество действительно свободных страниц.
Если процесс пытается обратиться к странице, которой не существует в его таблице страниц, но она имеется в одной из очередей страниц (например, в очереди неактивных страниц или очереди кэша), возникает сравнительно легко обрабатываемая ошибка отсутствия страницы, что приводит к повторной активации страницы. Если страницы вообще нет в системной памяти, то процесс должен быть блокирован, пока страница не будет взята с диска.
FreeBSD динамически изменяет свои очереди страниц, и пытается поддерживать разумное соотношение страниц в различных очередях, а также пытается поддерживать разумную схему работы с чистыми и грязными страницами. Количество случающихся перемещений зависит от нагрузки на память системы. Это премещение выполняется даемоном выгрузки страниц и затрагивает стирание грязных страниц (синхронизируя их с хранилищем), пометку страниц при активной работе с ними (обновляя их расположение в очередях LRU или перемещая их между очередями), постепенное перемещение страниц между очередями при нарушении баланса между очередями, и так далее. VM-система FreeBSD старается обработать достаточное количество ошибок доступа к странице для реактивации, чтобы определить реальную активность или простой страницы. Это приводит к повышению качества решений, принимаемых при стирании или выгрузке страницы в раздел подкачки.
Во FreeBSD реализована идея "объекта VM" общего вида. Объекты VM могут быть связаны с хранилищами различного типа--без долговременного хранения, с хранением в области подкачки, с хранением на физическом устройстве или с хранением в файле. Так как файловая система использует одни и те же объекты VM для управления внутренними данными, связанными с файлами, то в результате получается универсальный буферизирующий кэш.
Объекты VM могут затеняться. Это значит, что они могут выстраиваться один над другим. Например, вы можете иметь объект VM с хранением в области подкачки, который выстроен над VM-объектом с хранением в файле, для реализации операции mmap() типа MAP_PRIVATE. Такое построение также используется для реализации различных функций при совместном использовании, включая копирование при записи для разветвляющегося адресного пространства.
Должно быть отмечено, что vm_page_t может быть одновременно связана только с одним объектов VM. Затенение VM-объекта реализует эффективное совместное использование одной и той же страницы между несколькими экземплярами.
Объектам VM, хранящимся в vnode-узлах, таким, как объекты с хранением в файле, как правило, нужно поддерживать собственную информацию о чистоте/использованности независимо от предположении системы VM о чистоте/использованности. Например, когда система VM решает синхронизировать физическую страницу с ее хранилищем, то ей нужно помечать страницу как очищенную перед тем, как она действительно будет записана в хранилище. Кроме того, файловым системам нужно отображать части файла или метаданных файла в KVM для работы с ним.
Структуры, используемые для этого, известны как буферы файловой системы, struct buf, или bp. Когда файловой системе нужно произвести операцию с частью объекта VM, она обычно отображает часть объекта в struct buf и отображает страницы из struct buf в KVM. Таким же образом дисковый ввод/вывод обычно осуществляется отображением частей объектов в буферные структуры с последующим выполнением ввода/вывода этих структур. Низлежащие vm_page_t, как правило, на время ввода/вывода становятся занятыми, что удобно с точки зрения кода драйвера файловой системы, которому нужно будет работать с буферами файловой системы, а не прямо со страницами VM..
FreeBSD резервирует ограниченное число KVM для хранения отображений из struct bufs, но должно быть понятно, что эти KVM используются строго для хранения отображений и не ограничивают возможности по кэшированию данных. Кэширование физических данных является исключительно функцией vm_page_t, а не буферов файловой системы. Однако, так как буферы файловой системы являются заменителями ввода/вывода, они соответственно ограничивают количество выполняемых одновременно операций ввода/вывода. Так как обычно доступно несколько тысяч буферов файловой системы, то это, как правило, проблем не вызывает.
Во FreeBSD таблица расположения физических страниц отделена от системы VM. Все жесткие таблицы страниц для каждого процесса могут быть перестроены на лету и обычно являются временными. Специальные таблицы страниц, например, те, что управляют 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, что дает нам универсальный кэширующий буфер.
FreeBSD использует KVM для хранения различных структур ядра. Самым большим единственным объектом, хранящимся в KVM, является кэширующий буфер файловой системы. Это отображения, связанные с объектами struct buf.
В отличие от Linux, FreeBSD НЕ отображает всю физическую память в KVM. Это значит, что FreeBSD на 32-разрядных платформах может работать с конфигурациями памяти до 4ГБ. На самом деле, если бы аппаратный модуль управления памятью мог это делать, то на 32-разрядных платформах FreeBSD может теоретически работать с конфигурациями памяти до 8ТБ. Однако, так как большинство 32-разрядных платформ могут отображать только 4ГБ оперативной памяти, то этот вопрос остается теоретическим.
KVM управляется посредством нескольких методов. Основным механизмом, используемым для управления KVM, является zone allocator. Распределитель зоны берет кусок KVM и разделяет его на блоки памяти постоянного размера для того, чтобы выделить место под некоторый тип структуры. Вы можете воспользоваться командой vmstat -m для получения статистики текущего использования KVM до уровня зон.
Прилагались совместные усилия для того, чтобы сделать ядро FreeBSD самонастраивающимся. Обычно вам не нужно разбираться ни с чем, кроме параметров конфигурации ядра maxusers и NMBCLUSTERS. Это те параметры компиляции ядра, что указываются (обычно) в файле /usr/src/sys/i386/conf/CONFIG_FILE. Описание всех доступных параметров настройки ядра может быть найдено в файле /usr/src/sys/i386/conf/LINT.
В случае большой системы вам может понадобиться увеличить значение maxusers. Значения этого параметра, как правило, располагаются в диапазоне от 10 до 128. Заметьте, что слишком большое значение maxusers может привести к переполнению доступной KVM, что влечет за собой непредсказуемые результаты. Лучше задать некоторое разумное значение maxusers и добавить другие параметры, такие, как NMBCLUSTERS, для увеличения конкретных ресурсов.
Если ваша система будет интенсивно работать с сетью, вам может потребоваться увеличить значение NMBCLUSTERS. Обычно значения этого параметра находятся в пределах от 1024 до 4096.
Параметр NBUF традиционно использовался для масштабирования системы. Этот параметр определяет количество KVA, которое может использоваться системой для отображения буферов файловой системы для ввода/вывода. Заметьте, что этот параметр не имеет никакого отношения с единым кэшируемым буфером! Этот параметр динамически настраивается в ядрах версии 3.0-CURRENT и более поздних и не должен изменяться вручную. Мы рекомендуем вам НЕ пытаться задавать значение параметра NBUF. Позвольте сделать это системе. Слишком маленькое значение может привести к очень низкой эффективности операций с файловой системой, когда как слишком большое значение может истощить очереди страниц, так как слишком много страниц окажутся связанными.
По умолчанию ядра FreeBSD не оптимизированы. Вы можете задать флаги отладки и оптимизации при помощи директивы makeoptions в файле конфигурации ядра. Заметьте, что вы не должны использовать параметр -g, если не сможете использовать получающиеся при этом большие ядра (обычно превышающие размером 7МБ).
makeoptions DEBUG="-g" makeoptions COPTFLAGS="-O -pipe"
Утилита sysctl дает возможность изменить параметры ядра во время его работы. Как правило, вам не приходится работать ни с какими sysctl-переменными, особенно с теми, что относятся к VM.
Тонкая оптимизация VM и системы во время работы сравнительно проста и понятна. Сначала включите использование softupdates на ваших файловых системах UFS/FFS везде, где это возможно. В файле /usr/src/contrib/sys/softupdates/README находятся инструкции (и ограничения) по настройке этой функциональности.
Затем выделите достаточное количество пространства в области подкачки. Вы должны иметь на каждом физическом диске до четырех настроенных разделов подкачки, даже на "рабочих" дисках. Пространства в области подкачки должно быть не менее чем в два раза больше объема оперативной памяти, и даже больше, если памяти у вас не очень много. Вы должны также определять размер раздела подкачки, исходя из максимального объема оперативной памяти, который может быть на вашей машине, чтобы потом не выполнять разбиение диска повторно. Если вы хотите иметь возможность работы с дампом аварийного останова, то ваш первый раздел подкачки должен иметь объем, по крайней мере равный объему оперативной памяти, а в каталоге /var/crash должно быть достаточно свободного места для размещения дампа.
Подкачка поверх NFS прекрасно работает на системах версий -4.x и более поздних, но вы должны иметь в виду, что нагрузка от подкачки страниц ляжет на сервер NFS.