Виртуальная память

Виртуа́льная па́мять (англ. virtual memory) — это техника управления памятью, при которой каждой выполняющейся программе предоставляется собственное логическое адресное пространство, а отображение этого пространства на физическую оперативную память и на вторичное хранилище производится совместно аппаратурой процессора и операционной системой[1][2]. Прикладная программа обращается к данным по виртуальным адресам, не зная ни их физического размещения, ни того, какие участки её собственных данных в данный момент действительно находятся в ОЗУ; благодаря этому возможна одновременная работа множества процессов и адресация объёмов, превосходящих установленную оперативную память[3].

Общие сведения
Виртуальная память
англ. virtual memory
Область использования Операционные системы, архитектура ЭВМ, системное программирование
Дата появления 1956 (теоретическая концепция); декабрь 1962 (первая промышленная реализация — Atlas)
Место появления Берлинский технический университет; Манчестерский университет
Автор понятия Фриц-Рудольф Гюнтш

Определение и назначение

В современной литературе виртуальная память определяется как абстрактный уровень между логической моделью памяти, видимой программе, и реальным размещением данных в физических устройствах хранения[1]. Перевод адресов выполняет блок управления памятью (memory management unit, MMU), а политика размещения, замещения и защиты страниц определяется операционной системой[2][3].

Применение виртуальной памяти решает несколько связанных задач:

  • изоляция процессов — каждое приложение получает собственное непересекающееся адресное пространство, что предотвращает случайное или преднамеренное вмешательство в чужие данные и обеспечивает основу защиты памяти[1][4];
  • облегчение программирования — автоматическое перемещение данных между ОЗУ и диском избавляет разработчика от ручной разбивки программы на блоки, использовавшейся в технике оверлеев[5];
  • эффективное использование и защита памяти — физическая память распределяется по требованию, неактивные страницы вытесняются на вторичное хранилище, а права доступа (чтение, запись, исполнение) проверяются аппаратно для каждой страницы[3][6].

Общие принципы работы

Адрес, который программа записывает в регистр или указатель, называется виртуальным; ячейка ОЗУ, к которой в итоге происходит обращение, идентифицируется физическим адресом. Преобразование одного в другое осуществляет MMU, рассматривая виртуальный адрес как составной: старшие биты задают номер страницы (или сегмента), младшие — смещение внутри неё[3][7]. Для каждого процесса операционная система ведёт собственное отображение, поэтому одинаковые виртуальные адреса разных процессов могут указывать на разные физические страницы либо, наоборот, разделять общую страницу[4].

Роль операционной системы

ОС создаёт и обновляет таблицы страниц, распределяет физические фреймы между процессами, обрабатывает отказы страниц (page fault), реализует подкачку страниц и поддерживает права доступа[8][6]. При обращении к отсутствующей в ОЗУ странице аппаратура передаёт управление обработчику исключения; ядро выбирает кандидата на вытеснение в соответствии с алгоритмом замещения (LRU, второго шанса, рабочего множества) и загружает требуемую страницу из файла подкачки или из исполняемого образа[9].

Аппаратная поддержка

В подавляющем большинстве современных универсальных процессоров MMU и кэш переводов адресов — буфер ассоциативной трансляции (TLB) — интегрированы непосредственно в ядро процессора[10][7]. В то же время на микроконтроллерах и в значительной части жёстких систем реального времени виртуальная память либо отсутствует, либо применяется в усечённом виде: важнее предсказуемость задержек, чем удобство абстракции[11].

Влияние виртуальной памяти на производительность определяется в первую очередь стоимостью двух событий. Промах TLB требует обхода многоуровневой таблицы страниц и обычно стоит десятки тактов процессора; страничный отказ с обращением к диску — на несколько порядков дороже и может занимать миллионы тактов, поскольку включает физический ввод-вывод[12][13].

История

В 1940-е и начале 1950-х годов программисту, столкнувшемуся с нехваткой основной памяти, приходилось вручную разбивать программу на сегменты и организовывать их поочерёдную загрузку — технику, известную как оверлеи. Подбор оптимальной схемы оверлеев требовал глубокого знания машины и был хорошо известен как источник трудно воспроизводимых ошибок[5].

Первые концепции виртуальной памяти

Первое известное формализованное описание виртуальной памяти содержится в докторской диссертации немецкого физика Фрица-Рудольфа Гюнтша, представленной в Берлинский технический университет в мае 1956 года. Гюнтш описал машину с шестью блоками памяти на магнитных сердечниках по 100 слов и логическим адресным пространством в 1000 блоков; аппаратура должна была автоматически переносить блоки между основной памятью и магнитными барабанами, так чтобы программисту это движение оставалось невидимым[14].

Практические реализации

Первая работающая система со страничной виртуальной памятью была построена в Манчестерском университете в рамках проекта Atlas. Архитектура, названная авторами «одноуровневым хранилищем» (англ. one-level store), объединяла 16 384 слова основной памяти на сердечниках с 98 304 словами барабанной памяти; работающие прототипы появились в 1959 году, а официальная сдача первой машины состоялась в декабре 1962 года[15][16]. В 1961 году Burroughs Corporation выпустила B5000 — первый коммерческий компьютер с виртуальной памятью, в котором вместо страниц использовались сегменты переменного размера, описываемые аппаратными дескрипторами[17]. В 1965 году собственная реализация виртуальной памяти появилась в советской машине БЭСМ-6, однако 15-разрядное физическое адресное пространство процесса в ней не расширялось, что сдерживало развитие прикладного программирования[18].

Споры о том, способно ли автоматическое управление памятью конкурировать с тщательно настроенными оверлеями, были закрыты в 1969 году экспериментами группы Дэвида Сейра в IBM: измерения показали, что виртуальная память не уступает ручному управлению практически по всем интересным метрикам, а зачастую и превосходит его[19]. На рубеже 1970-х виртуальная память была положена в основу системы разделения времени IBM VM/370[20].

Распространение в мини-компьютерах и архитектуре x86

Первым мини-компьютером, в котором виртуальная память была доступна как коммерческая опция, считается Nord-1 норвежской компании Norsk Data (модуль виртуальной памяти предложен к 1969 году)[21]. В 1978 году DEC представила 32-разрядный суперминикомпьютер VAX-11/780 и операционную систему VAX/VMS с развитой страничной системой виртуальной памяти и алгоритмом замещения, опирающимся на рабочее множество[22].

В семействе x86 аппаратная виртуальная память впервые появилась в 80286 (1982) с введением защищённого режима; механизм опирался исключительно на сегментацию и плохо масштабировался для крупных сегментов[23]. Процессор 80386 (1985) добавил поверх сегментации страничный механизм с 4-килобайтной страницей и двухуровневой таблицей страниц; со временем именно страничный механизм стал основным средством защиты, а сегментация в 64-разрядном режиме перестала использоваться по своему первоначальному назначению[10][23].

Реализация виртуальной памяти

В подавляющем большинстве современных ОС виртуальная память организуется страницами фиксированного размера — обычно 4 КБ, с возможностью использовать большие страницы (2 МБ, 1 ГБ) для приложений с интенсивным трафиком к памяти[3][10]. Фиксированный размер страницы упрощает аппаратную реализацию преобразования адресов и исключает внешнюю фрагментацию, однако порождает внутреннюю фрагментацию: выделение даже одного байта на практике приводит к занятию целой страницы[3].

Схема преобразования адреса в общем виде такова: виртуальный адрес разбивается на номер виртуальной страницы (VPN) и смещение; VPN служит ключом к таблице страниц, которая возвращает номер физического фрейма (PFN); конкатенация PFN и смещения даёт физический адрес[3]. Чтобы не обращаться к таблице страниц при каждом доступе к памяти, процессор кэширует последние переводы в TLB; при промахе выполняется обход таблицы страниц (page walk), реализуемый либо аппаратно (как в x86), либо программно через обработчик ОС (как, например, в классических MIPS)[24][13]. Если требуемой страницы нет в ОЗУ, MMU генерирует исключение page fault, и ядро выполняет подкачку[9].

Если рабочее множество процессов в сумме превышает доступное ОЗУ, система может попасть в режим пробуксовки (англ. thrashing): практически всё время процессора уходит на обработку страничных отказов и собственно полезная работа не выполняется. Классическим средством борьбы с пробуксовкой служит модель рабочего множества, предложенная Питером Деннингом и используемая до сих пор в виде различных её вариаций[25][26].

Сегментная и странично-сегментная организация

При сегментной организации виртуальное пространство делится на сегменты переменного размера, отражающие логическую структуру программы — отдельный сегмент для кода, для стека, для динамических данных и т. д. Виртуальный адрес имеет вид пары ⟨g, s⟩, где g — номер сегмента, s — смещение внутри него; физический адрес получается сложением базы сегмента, прочитанной из таблицы сегментов, со смещением[27]. Для каждого сегмента независимо задаются права доступа, что естественным образом согласуется с логикой языка программирования, однако переменный размер сегментов приводит к внешней фрагментации физической памяти[27]. Страничная схема, напротив, устраняет внешнюю фрагментацию ценой внутренней.

На современных процессорах x86-64 сегментация в качестве механизма защиты практически не используется: в 64-разрядном режиме большинство сегментных регистров имеют нулевую базу и неограниченный предел, а защита и трансляция полностью лежат на страничном механизме[10][23]. В Microsoft Windows управление памятью построено не на сегментах, а на так называемых объектах-секциях (section objects) и страницах; секции описывают области, которые могут быть отображены в адресные пространства одного или нескольких процессов[28].

Существуют и странично-сегментные схемы, сочетающие логическое деление программы на сегменты с физической постраничной подкачкой внутри каждого сегмента. Такая модель была реализована в системе Multics на компьютере GE 645 и оказала большое влияние на проектирование последующих операционных систем, в том числе семейства Unix[29].

Виртуальная память в современных ОС

В настольных и серверных ОС виртуальная память — основной механизм управления памятью. В Linux каждый процесс получает независимое адресное пространство, страницы могут отображаться на файлы через системный вызов mmap(2), а вытесненные страницы записываются в раздел или файл подкачки; разделяемая память реализована через общие отображения и интерфейсы POSIX shared memory[30][8]. Microsoft Windows использует файл подкачки pagefile.sys и аналогичный механизм отображения файлов через секции[31][4]. В macOS на ядре XNU с версии 10.9 (Mavericks) применяется сжатие памяти (англ. memory compression): редко используемые страницы сначала сжимаются в ОЗУ и лишь при сильной нехватке памяти отправляются в swap-файлы[32][33].

Мобильные операционные системы существенно ограничивают подкачку на постоянное хранилище ради энергопотребления и износа флеш-памяти. В iOS страничный своп на диск не используется, а нехватка памяти обрабатывается подсистемой Jetsam, принудительно завершающей процессы при превышении пороговых значений нагрузки[32][34]. В Android аналогичную функцию выполняет демон lmkd (Low Memory Killer Daemon): он отслеживает давление на память и завершает наименее приоритетные процессы; одновременно ядро поддерживает сжатие памяти в ZRAM[35][36].

В микроконтроллерах и жёстких системах реального времени виртуальная память применяется ограниченно или не применяется вовсе: непредсказуемая задержка обработки страничного отказа несовместима с гарантированными сроками отклика, а сам механизм MMU увеличивает стоимость и энергопотребление кристалла[11].

Преимущества и ограничения

К преимуществам виртуальной памяти относят: упрощение программирования за счёт прозрачной подкачки данных; аппаратно-программную защиту памяти и изоляцию процессов; возможность одновременно выполнять программы, чьи суммарные потребности в памяти превышают физический объём ОЗУ; использование оперативной памяти как кэша для вторичного хранилища, в том числе при отображении файлов в память через mmap[1][30][6].

К ограничениям относят: накладные расходы на трансляцию адресов и обслуживание TLB; значительные задержки и риск пробуксовки при недостатке физической памяти; усложнение аппаратуры (MMU, многоуровневые таблицы страниц, IOMMU); плохую совместимость с требованиями жёсткого реального времени из-за непредсказуемости задержек, вызванных страничными отказами[13][25][11].

Галерея

Примечания

  1. 1 2 3 4 Arpaci-Dusseau R. H., Arpaci-Dusseau A. C. The Abstraction: Address Spaces. Operating Systems: Three Easy Pieces. University of Wisconsin–Madison. Дата обращения: 14 мая 2026.
  2. 1 2 Denning P. J. Virtual memory // ACM Computing Surveys. — 1970. — Т. 2, вып. 3. — С. 153–189. — doi:10.1145/356571.356573.
  3. 1 2 3 4 5 6 7 Arpaci-Dusseau R. H., Arpaci-Dusseau A. C. Paging: Introduction. Operating Systems: Three Easy Pieces. University of Wisconsin–Madison. Дата обращения: 14 мая 2026.
  4. 1 2 3 Russinovich M. Pushing the Limits of Windows: Virtual Memory. Microsoft Tech Community (21 июля 2008). Дата обращения: 14 мая 2026.
  5. 1 2 Denning P. J. Before Memory Was Virtual // In the Beginning: Recollections of Software Pioneers. — 1997. Архивировано 10 января 2017 года.
  6. 1 2 3 Gorman M. Understanding the Linux Virtual Memory Manager. The Linux Kernel Archives. Дата обращения: 14 мая 2026.
  7. 1 2 Translation tables in Armv8-A. Arm Limited. Дата обращения: 14 мая 2026.
  8. 1 2 Concepts overview — The Linux Kernel documentation. The Linux Kernel Archives. Дата обращения: 14 мая 2026.
  9. 1 2 Arpaci-Dusseau R. H., Arpaci-Dusseau A. C. Beyond Physical Memory: Mechanisms. Operating Systems: Three Easy Pieces. University of Wisconsin–Madison. Дата обращения: 14 мая 2026.
  10. 1 2 3 4 Intel 64 and IA-32 Architectures Software Developer’s Manual, Vol. 3A: System Programming Guide. Intel Corporation. Дата обращения: 14 мая 2026.
  11. 1 2 3 Stankovic J. A. Misconceptions About Real-Time Computing: A Serious Problem for Next-Generation Systems // IEEE Computer. — 1988. — Т. 21, вып. 10. — С. 10–19. — doi:10.1109/2.7053.
  12. Drepper U. What Every Programmer Should Know About Memory. Red Hat. Дата обращения: 14 мая 2026.
  13. 1 2 3 Corbet J. Huge pages part 5: A deeper look at TLBs and costs. LWN.net. Дата обращения: 14 мая 2026.
  14. Jessen E. Origin of the Virtual Memory Concept // IEEE Annals of the History of Computing. — 2004. — Т. 26, вып. 4. — С. 71–72. — doi:10.1109/MAHC.2004.56.
  15. Milestones:Atlas Computer and the Invention of Virtual Memory, 1957–1962. Engineering and Technology History Wiki, IEEE. Дата обращения: 14 мая 2026.
  16. Lavington S. The Atlas Milestone. Communications of the ACM. Дата обращения: 14 мая 2026.
  17. The Descriptor — A Definition of the B 5000 Information Processing System. Burroughs Corporation; UC Berkeley course archive. Дата обращения: 14 мая 2026.
  18. Королёв Л. Н. Архитектура ЭВМ БЭСМ-6. PARALLEL.RU. Дата обращения: 14 мая 2026. Архивировано 13 апреля 2001 года.
  19. Sayre D. Is automatic ‘folding’ of programs efficient enough to displace manual? // Communications of the ACM. — 1969. — Т. 12, вып. 12. — С. 656–660. — doi:10.1145/363626.363629.
  20. Creasy R. J. The origin of the VM/370 time-sharing system // IBM Journal of Research and Development. — 1981. — Т. 25, вып. 5. — С. 483–490. — doi:10.1147/rd.255.0483. Архивировано 7 мая 2016 года.
  21. Heradstveit P. The Founding, Fantastic Growth, and Fast Decline of Norsk Data AS. HAL / IFIP Advances in Information and Communication Technology. Дата обращения: 14 мая 2026.
  22. Levy H. M., Lipman P. H. Virtual Memory Management in the VAX/VMS Operating System // IEEE Computer. — 1982. — Т. 15, вып. 3. — С. 35–41. — doi:10.1109/MC.1982.1653970.
  23. 1 2 3 x86 memory segmentation. Wikipedia. Дата обращения: 14 мая 2026.
  24. Arpaci-Dusseau R. H., Arpaci-Dusseau A. C. Paging: Faster Translations (TLBs). Operating Systems: Three Easy Pieces. University of Wisconsin–Madison. Дата обращения: 14 мая 2026.
  25. 1 2 Denning P. J. The Working Set Model for Program Behavior // Communications of the ACM. — 1968. — Т. 11, вып. 5. — С. 323–333. — doi:10.1145/363095.363141.
  26. Denning P. J. Thrashing. Denning Institute. Дата обращения: 14 мая 2026.
  27. 1 2 Arpaci-Dusseau R. H., Arpaci-Dusseau A. C. Segmentation. Operating Systems: Three Easy Pieces. University of Wisconsin–Madison. Дата обращения: 14 мая 2026.
  28. Section Objects and Views. Microsoft Learn. Дата обращения: 14 мая 2026.
  29. Bensoussan A., Clingen C. T., Daley R. C. The Multics Virtual Memory: Concepts and Design // Communications of the ACM. — 1972. — Т. 15, вып. 5. — С. 308–318. — doi:10.1145/355602.361306.
  30. 1 2 mmap(2) — Linux manual page. man7.org. Дата обращения: 14 мая 2026.
  31. Virtual Address Spaces. Microsoft Learn. Дата обращения: 14 мая 2026.
  32. 1 2 About the Virtual Memory System. Apple Inc.. Дата обращения: 14 мая 2026.
  33. View memory usage in Activity Monitor on Mac. Apple Inc.. Дата обращения: 14 мая 2026.
  34. Levin J. No pressure, Mon! Handling low memory conditions in iOS and Mavericks. NewOSXBook. Дата обращения: 14 мая 2026.
  35. Low memory killer daemon. Android Open Source Project. Дата обращения: 14 мая 2026.
  36. Low memory killers. Android Developers. Дата обращения: 14 мая 2026.

Литература

Ссылки

Дополнительно по теме