Hardcore programmer
1.91K subscribers
9 photos
3 videos
22 links
Продвинутые темы из программирования и computer science. Особенности различных языков программирования. Глубокое погружение в software engineering.

Поддержать канал:
https://www.donationalerts.com/r/hardcore_programmer
Download Telegram
Channel created
Устройство виртуальной памяти процесса

Без понимания принципов устройства виртуальной памяти процесса вполне можно разрабатывать прикладное ПО. Однако, понимание этих принципов будет полезно любому software инженеру. А в случаях низкоуровневой работы с памятью такие знания становятся необходимостью. В данном посте я поверхностно раскрою данную тему, а в последующих сделаю углубление в отдельные темы о том, как работает память внутри программного обеспечения.

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

Если же мы имеем дело с программой исполняющейся под одной из современных операционных систем, таких как Linux, Windows, MacOS, FreeBSD и некоторых других, то мы оказываемся в многозадачной среде, в которой исполняются несколько программ параллельно.
В таких условиях при использовании реальной памяти появляется ряд проблем. Одна из них - это эффективное ограничение доступа к областям памяти принадлежащих другим процессам. Менее очевидная для разработчиков работавших только с высокоуровневыми языками проблема - мы не можем гарантировать, что исполняемый код нашей программы будет всегда загружен в оперативную память по одним и тем же адресам, что сильно затрудняет вызовы функций/методов нашей программы.
Наличие же виртуальной памяти, помимо решения вышеуказанных проблем позволяет делать еще несколько полезных трюков, вроде сброса неиспользуемой в данный момент памяти в файл/раздел подкачки или архивирование ее в ZRAM, а так же отображение виртуальной памяти в устройства отличные от оперативный памяти.

Как же это работает?
Каждому процессу в системе предоставляется виртуальное адресное пространство, как будто он обладает максимально возможной памятью для данной аппаратной архитектуры. О том, какие максимумы для памяти доступны на различных архитектурах я расскажу в одном из следующих постов. А вот с виртуальными адресами разберемся прямо сейчас.
Когда процесс обращается к памяти по своему виртуальному адресу, контроллер памяти сопоставляет ему реальный адрес по специальным таблицам, составленным операционной системой.
Если такое сопоставление успешно, то программа получает в свое распоряжение конкретный блок реальной памяти.
Если же виртуальный адрес не сопоставлен реальному, то программа падает с segmentation fault.
Еще один возможный сценарий, когда виртуальный адрес сопоставлен с чем либо за пределами оперативной памяти, например с файлом подкачки, в этом случае управление передается операционной системе, которая загружает нужные данные в оперативную память и обновляет таблицу виртуальных адресов, после чего операция повторяется и программа получает доступ к требуемой памяти.

Мы рассмотрели самые азы работы памяти процесса, что такое виртуальная память, зачем она нужна и как работает. Однако, работа с памятью имеет огромное число нюансов, а их понимание важно для понимания того, как работают программы, которые мы пишем, ведь суть любой программы сводится к манипуляциям с данными. В последующих постах я расскажу о многих из таких нюансов более подробно.
👍50❤‍🔥5🔥32
Страничная организация памяти

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

Для начала посмотрим, как это работает.
Для каждого процесса ОС заводит несколько таблиц для хранения информации о страницах памяти. В одной таблице могут хранится только таблицы одинакового размера, а количество записей ограничено. Помимо реального адреса начала страницы, в каждой записи таблицы хранятся права доступа к данной странице, похожие на права файлов в unix - чтение, запись, выполнение, с той разницей, что ограничение на чтение обычно не имеет смысла и вместо него используется ограничение на доступ только из ядра ОС.
Несколько таблиц объединяются в каталоги, а каталоги могут объединяться в каталоги более высокого уровня. В современных 64-битных архитектурах может использоваться до 4 уровней вложенности.
Виртуальный адрес в такой модели состоит из двух частей: старшие биты указывают на индекс каталога/таблицы, а младшие - смещение в странице.

За счет фиксированного размера страниц в одной таблице количество младших бит отвечающих за смещение постоянно. При размещении страниц по реальным адресам, у которых данные биты будут нулями, нам не потребуется дополнительной арифметики со смещением от начала страницы, что и делает данный подход эффективным.
Помимо страничной организации памяти в некоторых ОС так же используется сегментная организация памяти, которая может быть более привлекательной за счет совпадения логических областей памяти с сегментами, но на деле оказывается менее эффективной, так как сегменты могут быть произвольного размера и часто требуют дополнительной арифметики при пересчете виртуальных адресов в реальные.
👍14🔥54❤‍🔥2