LINUX.ORG.RU
ФорумTalks

Обьясните по работе виртуальной памяти

 


0

1

Алоха. Я вот задумался. Вики сообщает: «<...>Допустимые в реализациях физические и математические адреса часто ограничены значениями 48 бит (256 терабайтов)<...>», это значит что я могу в своей программе использовать диапазон адресов 0x000000000000-0xFFFFFFFFFFFF и таким образом объём виртуальной памяти составляет 256 ТБ? Но ведь фактически её то меньше (включая swap)

Я низкоуровневым программированием не занимался никогда, поэтому мне эта ситуация не вполне понятна. Получается что верно одно из следующих утверждений:

1. ОС позволяет работать программе с 256 ТБ. Но это невозможно

2. Диапазон адресов виртуальной памяти ограничен её фактическим объёмом и команда mov 0xFFFFFFFFFFFF 0xFFFFFFFFFFFE всегда будет давать ошибку, так как это выходит за объём доступной памяти

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

Так как же сделано на самом деле?

Пустые страницы не занимают физическую память. Когда физическая память заканчивается, система начинаеть жонглировать swap-ом. Когда заканчивается swap - кто-нибудь умирает.

Deleted
()

Есть то, что программа видит — это те самые 256 терабайт.

Есть то, что на самом деле — это какие-нибудь 16 гигов, например.

И есть табличка, на каждую программу — как эти самые 256 терабайт разместить в 16 гигах.

Понятно, что разместить полностью невозможно. Поэтому табличка содержит информацию только про часть этих 256 терабайт. Плюс там же есть записи «эта часть 256 терабайт на самом деле находится на диске».

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

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

В итоге программа вообще не знает обо всяких таблицах, свопах, и т.д. Она считает, что работает с 256 терабайтами.

Miguel ★★★★★
()

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

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

Схема обустройства адресного пространства процесса зависит от ОС, версии, платформы и опций ядра. Даже в Линуксе оно бывает очень разным. Пример:

di@di:~$ pmap 13750
13750:   /bin/bash -l
0000000000400000   1024K r-x-- bash
0000000000700000     12K r---- bash
0000000000703000     36K rw--- bash
000000000070c000     40K rw---   [ anon ]
000000000253f000   1764K rw---   [ anon ]
00007ff8c5796000     40K r-x-- libnss_files-2.24.so
00007ff8c57a0000   2048K ----- libnss_files-2.24.so
00007ff8c59a0000      4K r---- libnss_files-2.24.so
00007ff8c59a1000      4K rw--- libnss_files-2.24.so
00007ff8c59a2000     24K rw---   [ anon ]
00007ff8c59a8000     44K r-x-- libnss_nis-2.24.so
00007ff8c59b3000   2044K ----- libnss_nis-2.24.so
00007ff8c5bb2000      4K r---- libnss_nis-2.24.so
00007ff8c5bb3000      4K rw--- libnss_nis-2.24.so
00007ff8c5bb4000     80K r-x-- libnsl-2.24.so
00007ff8c5bc8000   2048K ----- libnsl-2.24.so
00007ff8c5dc8000      4K r---- libnsl-2.24.so
00007ff8c5dc9000      4K rw--- libnsl-2.24.so
00007ff8c5dca000      8K rw---   [ anon ]
00007ff8c5dcc000     28K r-x-- libnss_compat-2.24.so
00007ff8c5dd3000   2044K ----- libnss_compat-2.24.so
00007ff8c5fd2000      4K r---- libnss_compat-2.24.so
00007ff8c5fd3000      4K rw--- libnss_compat-2.24.so
00007ff8c5fd4000   2860K r---- locale-archive
00007ff8c629f000   1620K r-x-- libc-2.24.so
00007ff8c6434000   2048K ----- libc-2.24.so
00007ff8c6634000     16K r---- libc-2.24.so
00007ff8c6638000      8K rw--- libc-2.24.so
00007ff8c663a000     16K rw---   [ anon ]
00007ff8c663e000     12K r-x-- libdl-2.24.so
00007ff8c6641000   2044K ----- libdl-2.24.so
00007ff8c6840000      4K r---- libdl-2.24.so
00007ff8c6841000      4K rw--- libdl-2.24.so
00007ff8c6842000    152K r-x-- libtinfo.so.5.9
00007ff8c6868000   2044K ----- libtinfo.so.5.9
00007ff8c6a67000     16K r---- libtinfo.so.5.9
00007ff8c6a6b000      4K rw--- libtinfo.so.5.9
00007ff8c6a6c000    140K r-x-- ld-2.24.so
00007ff8c6c67000      8K rw---   [ anon ]
00007ff8c6c85000     28K r--s- gconv-modules.cache
00007ff8c6c8c000     12K rw---   [ anon ]
00007ff8c6c8f000      4K r---- ld-2.24.so
00007ff8c6c90000      4K rw--- ld-2.24.so
00007ff8c6c91000      4K rw---   [ anon ]
00007ffe398d8000    132K rw---   [ stack ]
00007ffe399b2000     12K r----   [ anon ]
00007ffe399b5000      8K r-x--   [ anon ]
 total            22516K
di@di:~$

Собственно память - это anon и stack. Каждая виртуальная страница может указывать на какую-то физическую, находящуюся в RAM или swap. Причем много виртуальных могут указывать на одну физическую. Когда anon выделяется при помощи mmap(), то изначально все страницы этого куска указывают на одну и ту же физическую пустую страницу (специальную). Потом по мере использования (записи в память) выделяются отдельные физические страницы.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

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

Shadow ★★★★★
()
Последнее исправление: Shadow (всего исправлений: 1)
Ответ на: комментарий от Shadow

Разве? vm.swappiness = 1 не заставит ядро терпеть до последнего? Ну и даже если прав ты, то моё утверждение твоему не противоречит.

Deleted
()
Ответ на: комментарий от Deleted

Ну, это в Linux так и вообще, идиотская практика.

Shadow ★★★★★
()
Ответ на: комментарий от Deleted

Я просто дополнил - часто полезно это знать. А то отключают своп на 32 гб, и удивляются, почему тормозит.

Shadow ★★★★★
()
Ответ на: комментарий от Shadow

Да, это полезное замечание. Но 32Гб - это же совсем дохрена, чем там своп оправдан? Или речь не о десктопе?

Deleted
()
Ответ на: комментарий от Deleted

Впрочем, не важно, он в любом случае нужен как страховка.

Deleted
()

Крайне рекомендую не полагаться на лоровских аналитиков а просто прочитать третью главу «Современные операционные системы» Эндрю Таненбаума, первых частей будет достаточно для базового понимания работы виртуальной памяти, накладных расходов и ограничений. И это будет в разы лучше чем бедный пересказ полученный тут.

В кратце - память(даже выделенная) якобы есть, но где она в данный момент(физически), и можно ли производить с ней i/o зависит только от наличия ресурсов в системе и того был ли уже доступ к конкретной странице. Поэтому, если хочется не получить segfault при использовании большого буфера, который вроде бы и выделен, но используется не сразу весь, будет хорошей идеей про инициализировать его например нулями(портабельный способ) или использовать специальное системное api, что бы попросить на самом деле выделить память.

pon4ik ★★★★★
()
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от Deleted

Если ты забьешь 32 оперативки, ядро без свопа просто не сможет выделить физическую память СРАЗУ. Хоть сделать своп в памяти, но нужно.

Shadow ★★★★★
()

Так как же сделано на самом деле?

На самом деле есть много вариаций и даже комбинаций (например с 64 разрядными процессорами и 32 разрядными операционками поддерживающими PAE режим).

Для каждой архитектуры процессоров есть свои вариации.

В большинстве случаев это именно хардварно зависимо от процессоров и чипсетов, а не от операционки, как многие себе вообразили.

Пончик правильно сказал. И я поддерживаю. Лучше прочти сотни страниц литературы и не только того автора, чем ожидай, что кто-то здесь даст полный ответ в 2 предложения.

Я в своё время изучал углублённо архитектуру i486 проца. И там ой как всё не просто. Хотя в принципе виртуальная память работает так же как на первых процах с «нормальным» защищённым режимом i386. Вплоть до пней 4 вообще. На всех 32 разрядных процах тоесть.

На 64 разрядных процах вариаций стало больше.

Serg_HIS
()

Некоторое подобие свопа даже было на 8 разрядных i8086.

Но то было не аппаратно, а программно и сегодня уже почти не правда :) Можешь почитать про оверлеи. Фалы с расширением OVL под DOS.

Serg_HIS
()
Ответ на: комментарий от Serg_HIS

поправочка

на 8 разрядных i8088.

но да и на 16 разрядных i8086, которые ни чем не отличались по командам и адресации памяти, а только пропускной способностью шины данных.

Serg_HIS
()
Ответ на: комментарий от Nastishka

Если на 16+16своп оом не приходил, что он забыл на 32 без свопа?

TheAnonymous ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.