Чак Сильверс. UBC: Эффективная объединённая подсистема ввода-вывода и кэширования памяти для NetBSD, 2000

Аннотация

В этом документе представлен UBC (Unified Buffer Cache - объединённый буферный кэш) - проект объединения кэшей файловой системы и данных файлов виртуальной памяти, увеличивающий производительность системы. Здесь обсуждаются как традиционные интерфейсы кэширования BSD, так и новые интерфейсы UBC, особое внимание уделяется принятым проектным решениям. Мы также обсудим архитектуру сходных решений из других операционных систем, уделяя особое внимание практическим сторонам этих отличий. Этот проект всё ещё находится в стадии разработки и по завершении войдёт в будущий выпуск NetBSD.

1. Введение

Современные операционные системы позволяют получать доступ к данным файловой системы двумя способами: через отображение в память и через системные вызовы подсистемы ввода-вывода read() и write(). В традиционных операционных системах типа UNIX запросы к отображению в памяти обрабатываются подсистемой виртуальной памяти, а системные вызовы обрабатываются подсистемой ввода-вывода. Традиционно эти две подсистемы разрабатывались раздельно и поэтому не глубоко интегрированы друг с другом. Например, в операционной системе NetBSD [1] подсистема виртуальной памяти UVM [2] и подсистема ввода-вывода оснащены собственными механизмами кэширования данных, которые работают почти независимо друг от друга. Недостаток интеграции ведёт к снижению совокупной производительности и гибкости системы. Для достижения хорошей производительности важно чтобы подсистемы виртуальной памяти и ввода-вывода были тесно интегрированы. Эту интеграцию реализует UBC.

2. Предыстория

Для понимания вносимых UBC улучшений важно сначала разобраться, как обстояло дело до появления UBC. Для начала поясним несколько терминов:

Главные интерфейсы для доступа к данным файлов - это:

В NetBSD без UBC системные вызовы read() и write() реализованы с использованием буферного кэша. Системный вызов read() читает данные файла в буферный кэш и затем копирует их в приложение. Однако системный вызов mmap() использует для хранения данных страничный кэш, т.к. память буферного кэша не управляется системой виртуальной памяти и не может быть отображена на адресное пространство приложения. Поэтому данные файла из буферного кэша копируются в страничный кэш, чтобы использовать их для исправления ошибок доступа к страницам отображения приложения. Для записи на диск изменённых данных страничного кэша новая версия данных копируется обратно в буферный кэш и из него записывается на диск. На рисунке 1 показан поток данных между диском и приложением при наличии традиционного буферного кэша.

Такое двойное кэширование данных значительно снижает эффективность системы. Наличие двух копий данных означает, что используется в два раза больше памяти и что приложениям доступно меньше памяти. Копирование данных туда и обратно между буферным и страничным кэшами создаёт нагрузку на центральный процессор, вымывает данные из его кэшей и в общем отрицательно влияет на производительность. Наличие двух копий данных также создаёт риск несоответствия копий, что может привести к трудноуловимым проблемам в приложениях.

Т.к. буферный кэш имеет постоянный объём, он плохо подходит как для большого объёма данных, потому что может оказаться слишком мал (что будет приводить к частым промахам), так и для небольшого объёма данных, для которого может оказаться слишком велик (из-за чего память будет недоступной для других нужд).

Данные буферного кэша отображаются в виртуальное адресное пространство ядра. Т.к. современное оборудование может иметь больше оперативной памяти, чем имеется в виртуальном адресном пространстве ядра, возникает искусственное ограничение.

Для решения этих проблем во многих операционных системах способ использования страничного и буферного кэшей был изменён. В каждой системе использовались собственные решения, поэтому мы сначала опишем UBC, а затем рассмотрим решения из нескольких других популярных операционных систем.

3. Итак, что же такое UBC?

UBC - это новая подсистема, которая решает проблему двух кэшей. В UBC данные файла хранятся в страничном кэше и для системных вызовов read()/write() и для системного вызова mmap(). Данные файла читаются сразу в страничный кэш без прохождения через буферный кэш. Для этого вводятся два новых VOP'а, которые возвращают страницы с требуемыми данными из страничного кэша, при необходимости запрашивая драйвер устройства прочитать данные с диска. Т.к. страницы из страничного кэша должны принадлежать отображению в памяти, введён новый механизм для создания временных отображений, чтобы системные вызовы read() и write() могли скопировать данные файла в адресное пространство приложения. На рисунке 2 показаны изменения потока данных с появлением UBC.

Рисунок 1: NetBSD до появления UBC.
Рисунок 2: NetBSD с появлением UBC.

UBC вводит новые интерфейсы:

Кроме этих новых интерфейсов было внесено несколько изменений в существующую архитектуру UVM для её полировки.

Ранее в UVM структуры vnode и uvm_object не были взаимозаменямыми. Несколько их полей дублировались и поддерживались независимо в каждой из них. Эти дублирующиеся поля были объединены. При первом использовании структуры vnode в качестве структуры uvm_object пока ещё нужна дополнительная инициализация, но в конечном итоге она будет удалена.

Ранее UVM поддерживал в структурах uvm_object только 32-битные смещения, поэтому в страничный кэш можно было сохранить данные только первых 4 гигабайта файла. Это не было большой проблемой, потому что существует не так много программ, желающих получать доступ к смещениям после 4 гигабайт при помощи mmap(). Но теперь системные вызовы read() и write() для доступа к данным тоже используют интерфейсы страничного кэша. Чтобы позволить обращаться к файлам по смещениям больше 4 гигабайт, в uvm_object была реализована поддержка 64-битных смещений.

4. Что было сделано в других операционных системах?

Решаемая UBC проблема существует долгое время, с тех пор как в конце 1980'х появился доступ к файлам через отображения в памяти. В большинстве операционных систем типа UNIX эта проблема так или иначе была решена, способы решения значительно отличаются.

Впервые к этой проблеме обратились разарботчики операционной системы SunOS [4, 5]. По большому счёту в UBC копируются её архитектурные решения. Основные отличия архитектур кэша SunOS и системы UBC проистекают из разницы между системами виртуальной памяти SunOS и UVM. Но т.к. абстракции средств подкачки UVM и драйверов сегментов SunOS похожи, их влияние на архитектуру довольно мало.

Когда два года назад впервые началась работа над UBC, в качестве прототипа рассматривалась так же операционная система FreeBSD [6], где эта проблема тоже уже решена. В FreeBSD для доступа к данным файла продолжают использоваться интерфейсы буферного кэша, но данные хранятся в страничном кэше, а не в отдельной области памяти. В результате те же физические страницы можно получить из файла и через интерфейс буферного кэша и через интерфейс страничного кэша. Такой подход выгоден тем, что не нужно переделывать имеющиеся реализации файловых систем. Связующий код трансляции между интерфейсами так же сложен, как связующий код из SunOS, но в нём не была решена проблема взаимоблокировки (когда приложение одновременно вызывает write() и изменяет этот же файл через отображение в памяти), поэтому предпочтение было отдано решению из SunOS.

Подход Linux [7] (версии 2.3.44 - последней версии на момент написания этого документа) также очень похож на подход SunOS. Данные файла сохраняются только в страничном кэше. Временные отображения страничного кэша для системных вызовов read() и write() не требуются, т.к. Linux всегда отображает всю физическую память в виртуальное адресное пространство ядра. Одна из интересных особенностей заключается в том, что при сохранении страниц диска в кэше Linux добавляет номера блочных устройств в виде списка структур buffer_head. При изменении страница записывается обратно на диск, запросы на выполнение операций ввода-вывода можно отправить сразу драйверу устройства, без чтения дополнительных блоков для определения места, в которое должны быть записаны данные страницы.

Последней рассмотренной системой была HP-UX, в которой проблема кэширования данных файловой системы решена совершенно иначе. HP-UX продолжает сохранять данные файла и в буферном кэше и в страничном кэше, хотя и избегает дополнительного копирования данных, имевшегося в NetBSD до UBC, при чтении данных с диска прямо в страничный кэш. Такое решение обоснуется тем, что к большинству файлов обращаются либо через системные вызовы read()/write(), либо через системный вызов mmap(), но не через оба сразу. Пока оба механизма хорошо работают поотдельности, нет необходимости перепроектировать HP-UX для устранения проблемы согласованности. Некоторые попытки избежать рассогласованности между кэшами предпринимались, но использование блокировки не позволяет достичь максимальной эффективности.

Есть и другие операционные системы, в которых реализован объединённый кэш (например, в Compaq Tru64 UNIX и в IBM AIX), но мы не смогли найти информацию об архитекутре этих операционных систем для сравнения.

5. Производительность

Т.к. работа над UBC ещё не завершена, точная оценка производительности была бы преждевременной. Однако мы провели несколько простых сравнений достигнутого состояния. Для тестов использовался компьютер с 333-мегагерцовым процессором Pentium II, 64 мегабайтами оперативной памяти и IDE-диском объёмом 12 гигабайт. Для оценки скорости последовательного чтения и записи использовалась последовательность команд dd. Мы создали файл размером 1 гигабайт (что значительно больше физического объёма оперативной памяти, доступной для кэширования). Затем мы перезаписали этот файл, чтобы оценить скорость, с которой данные, изменённые с помощью системного вызова write(), попадут на диск без издержек на выделение блоков файла. Затем мы снова прочитали весь файл, чтобы оценить насколько быстро файловая система может извлечь данные с диска. Наконец, мы несколько раз прочитали первые 50 мегабайт файла (которые должны полностью уместиться в физической памяти), чтобы определить ускорение доступа к данным в кэше. Результаты тестов сведены в таблице 1.

Таблица 1: Сравнение производительности UBC.
  Время выполнения теста (в секундах)
Ввод Вывод Размер NetBSD NetBSD FreeBSD Linux
  1.4.2 с UBC 3.4 2.2.12-20smp
устройство /dev/null 1 Гб 72.8 72.7 279.3 254.6
/dev/zero новый файл 1 Гб 83.8 193.0 194.3 163.9
/dev/zero перезапись файла 1 Гб 79.4 186.6 192.2 167.3
нерезидентный файл /dev/null 1 Гб 72.7 86.7 279.3 254.5
нерезидентный файл /dev/null 50 Мб 3.6 4.3 13.7 12.8
резидентный файл /dev/null 50 Мб 3.6 0.8 4.1 11.5
см. выше /dev/null 50 Мб 3.6 0.8 0.7 4.5
см. выше /dev/null 50 Мб 3.6 0.8 0.7 0.8
см. выше /dev/null 50 Мб 3.6 0.8 0.7 0.8

Большая разница в результатах первых четырёх тестов на трёх операционных системах без UBC возникает из-за отличий в производительности их дисковых драйверов IDE. Все операционные системы, за исключением NetBSD с UBC, во время тестов выполняли последовательное буферизованное чтение большого файла с устройства с одинаковой скоростью, поэтому всё что мы можем в действительности сказать из этого, что другая архитектура кэширования не добавляет сколь-нибудь значительных издержек. При чтении система UBC не достигает скорости устройства, поэтому тут есть возможности для улучшений. Нужен дополнительный анализ, чтобы выяснить причины замедления.

Очевидно UBC требует улучшения производительности записи. Частично проблема вызвана тем, что UVM при нехватке памяти недостаточно эффективно сбрасывает изменённые страницы, а частично тем, что код файловой системы в настоящее время не переупорядочивает асинхронные операции записи на диск в непрерывную последовательность. Мы сосредоточились на производительности чтения, поэтому плохая производительность записи не была неожиданной.

Наибольший интерес представляют тесты, в которых 5 раз читаются одни и те же 50 мегабайт файла. Из них явно видна выгода от увеличения памяти, доступной для кэшировани в системе UBC по сравнению с NetBSD. В NetBSD 1.4.2 все пять чтений происходят со скоростью устройства, в то время как во всех других системах через несколько запусков достигнута скорость чтении из памяти. У нас нет объяснения, почему FreeBSD и Linux не выполняют второе чтение 50 мегабайт со скоростью памяти и почему Linux не делает этого даже с третьей попытки.

6. Заключение

В этом документе представлена система UBC, улучшающая кэширование файловой системы и виртуальной памяти для NetBSD. Эта система содержит много улучшений по сравнению с прежней архитектурой NetBSD:

7. Доступность

Как только работа будет завершена, она станет частью будущего релиза NetBSD. В настоящее время исходный код находится в ветке "chs-ubc2" дерева CVS NetBSD и может быть получен анонимно. За подробностями обратитесь по ссылке https://www.netbsd.org/Sites/net.html.

Идёт активный процесс разработки, предстоит выполнить ещё больше работы! Запланирована следующая работа:

8. Благодарности

Хотим поблагодалить всех, кто помог с обзором черновиков этого документа. Отдельное спасибо Чаку Кранору!

Библиография

  1. Проект NetBSD.

    Операционная система NetBSD.

    Более подробную информацию см. по ссылке https://www.netbsd.org.

  2. C. Cranor and G. Parulkar / Чарльз Кранор, Гурудатта Парулкар.

    The UVM Virtual Memory System / Система виртуальной памяти UVM

    In Proceedings of the 1999 USENIX Technical Conference, June 1999.

  3. Marice J. Bach.

    The Design of the UNIX Operating SystemПП1.

    Prentice Hall, February 1987.

  4. J. Moran, R. Gingell and W. Shannon.

    Virtual Memory Architecture in SunOS.

    In Proceedings of USENIX Summer Conference, pages 81-94. USENIX, June 1987.

  5. J. Moran.

    SunOS Virtual Memory Implementation.

    In Proceedings of the Spring 1988 European UNIX Users Group Conference, April 1988.

  6. Проект FreeBSD.

    Операционная система FreeBSD.

    Более подробную информацию см. по ссылке https://www.freebsd.org.

  7. Л. Торвальдс и другие.

    Операционная система Linux.

    Более подробную информацию см. по ссылке https://www.linux.org.

Примечания переводчика

  1. В сети имеется широко растиражированный неофициальный перевод книги, выполненный кандидатом технических наук Кафедры Вычислительной техники Санкт-Петербургского Института Точной Механики и Оптики Крюковым А. В.: 1, 2, 3.

Автор перевода на русский язык: Владимир Ступин