Как sbrk/brk реализован в Linux?

Я думал о том, как ядро ​​Linux реализует системные вызовы, и мне было интересно, может ли кто-нибудь дать мне общее представление о том, как работает sbrk / brk?

Я просмотрел код ядра, но его так много, и я его не понимаю. Я надеялся на резюме от кого-то?

4 ответа

Решение

На очень высоком уровне ядро ​​Linux отслеживает память, видимую для процесса, как несколько "областей памяти" (struct vm_area_struct). Существует также структура, которая представляет (опять же на очень высоком уровне) все адресное пространство процесса (struct mm_struct). Каждый процесс (кроме некоторых потоков ядра) имеет ровно один struct mm_structчто, в свою очередь, указывает на все struct vm_area_struct для памяти, к которой он может получить доступ.

sys_brk системный вызов (находится в mm/mmap.c) просто настраивает некоторые из этих областей памяти. (sbrk это фантик вокруг brk). Это делается путем сравнения старого значения brk адрес (находится внутри struct mm_struct) и запрошенное значение.

Было бы проще взглянуть на mmap семейство функций во-первых, так как brk это частный случай этого.

Вы должны понимать, как работает виртуальная память, и как отображение MMU связано с реальной оперативной памятью.

Реальная оперативная память делится на страницы, традиционно 4 КБ каждая. каждый процесс имеет свое собственное отображение MMU, которое представляет этому процессу линейное пространство памяти (4 ГБ в 32-разрядной версии Linux). конечно, не все они на самом деле выделены. сначала он почти пустой, то есть ни одна реальная страница не связана с большинством адресов.

когда процесс достигает нераспределенного адреса (либо пытается прочитать, записать или выполнить его), MMU генерирует ошибку (аналогично прерыванию), и вызывается система VM. Если он решает, что какая-то ОЗУ должна быть там, он выбирает неиспользуемую страницу ОЗУ и связывается с этим диапазоном адресов.

таким образом, ядру все равно, как процесс использует память, а процессу на самом деле все равно, сколько в нем оперативной памяти, оно всегда будет иметь одинаковые линейные 4 ГБ адресного пространства.

теперь brk/sbrk работать на немного более высоком уровне: в принципе любой адрес памяти "за пределами" этой метки недопустим и не получит страницу ОЗУ, если к ней обращаются, вместо этого процесс будет уничтожен. библиотека userspace управляет выделением памяти в пределах этого предела и только при необходимости просит ядро ​​увеличить его.

Но даже если процесс начался с установки brk до максимально допустимого, он не будет выделять реальные страницы ОЗУ, пока не начнет получать доступ ко всем этим адресам памяти.

Что ж, с точки зрения сверхвысокого уровня ядро ​​выделяет блок памяти с возможностью постраничного вывода, модифицирует таблицы страниц процесса, запрашивающего этот блок, так, чтобы память отображалась в пространстве VA процесса, а затем возвращает адрес.

Ключевой концепцией того, как ядро ​​Linux передает память пользовательскому процессу, является то, что доступная куча процессов (сегмент данных) растет снизу вверх. ядро не отслеживает отдельные фрагменты памяти, только непрерывный блок памяти. Системные вызовы brk / sbrk увеличивают объем памяти, который есть у процесса, но процесс должен управлять им в виде полезных частей.

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

Другие вопросы по тегам