FreeBSD, 04 лекция (от 23 октября)
Материал из eSyr's wiki.
Allena (Обсуждение | вклад)
(Новая: Под управлением памятью мы понимаем вопросы аллокации. Три задачи: 1. маллок приложений -- практическ...)
К следующему изменению →
Версия 21:45, 23 октября 2013
Под управлением памятью мы понимаем вопросы аллокации.
Три задачи:
1. маллок приложений -- практически не коснемся 1. маллок внутри ядра, там же мы тоже хотим не вруную памятью заниматься 1. как ос создает процессу виртуальное адресное пространство.
Еще одна тема -- как виртуальная память соотносится с физической.
В компьютере есть какая-то память в слотах, у неё есть физические адреса. Если мы переставим из одного слота другой образуются дыки, и программировать в таких условиях очень часто. Во всех процессорах,под которые пишутся ос общего назначения есть mmu -- транслятор фиртуальных адресов в физические.
Ядро процессора в какой-то момент переходит в режим, когда используются виртуальные адреса, а не физические.
Процессор постоянно обращается к мму.
Какие то части виртуального пространства имеют отображение в физпамять, какие-то не имеют. Те которые не имеют вызовут у мму трап.
Страница -- гранулярность того, как мы можем делать отображение виртуальной памяти в физическую. Отображение само хранится тоже в памяти. Мму надо сходить к физическому адресу, прописаному в регистре процессора.
У процессора есть дополнительный кэш, специально предназначенный для мму.
Картинка виртуального пространства процесса.
Нулевой указатель никогда не замаплен.
дальше замаплен скомпилированный код -- program text
Дальше инициализированные данные.
Дальше идет heap -- память, которую процесс просит у ядра динамически.
Наверху есть конец памяти доступный процессу.
В фрибсд есть утилита procstat, которая позволяет увидеть это все своими глазами.
Удобно смотреть на /rescue/cat , потому что для динамически слинкованных приложений оно выглядит слишком сложно.
Как растет стек?
Когда пейдж фолт происходит по следующей странице за концом стека, этот пейдж фолт не завершает программу, а ядро аллоцирует еще одну страничкуи программа проолжает работать.
Важнео замечание -- ядро никак не может узнать, что вы перестали стеком пользоваться, и никогда обратно их не освободит, в лучшем случае высвопит.
Как растет куча?
исторически был вызов sbrk, который позволял руками уведличивать и уменьшать указательи конца кучи.
Старые маллоки на нем были основаны. Была проблема -- данные могли быть аллоцированы в конце, а до них все уже освободилось.
Современные маллоки основаны на mmap.
mmap можно делать не только для файлов, но и для просто регионов памяти.
Современные маллоки работают на ммапе, и под каждое ядро делают свою аренду.
В опенбсд всегда был очень хороший либцшный маллок. Сейчас jeamllloc. Настолько эффективный, что яндекс с трудом переходит на линукс, потому что они запускают приложение на линуксе и оно жрет в полтора раза больше памяти.
Фейсбук поступил хитрее -- у них работает автор этого маллока и специальноц для них пишет версию под линуксом.
Пример карты памяти динамически слинкованной программы.
В прошлой лекции у нас был указатель на vm_space он состоит из vm_map, vm_pmap -- одна отвечает за виртуальную память, другая за реальную.
Начнем придумывать как вм мап будет выглядеть.
vm_map -- список многочисленных ентри, начало и конец. Каждая мап_ентри должна что-то иметь под собой, какой-то объект, который описывает то что под ней лекциижит. Эта структура vm_object, она хранит уже реальные страницы, к которым можно доступиться. Также обжект хранит пейджер -- механизм, который позволяет загружать страницы, когда это требуется. вм_пейджы появляются в вм_ообжекте по мере пейдж_фолтов.
Так как страниц может быть очень много, они реализованы в виде дерева, с помощью алгоритма радикс.
Раз уж мы дошли достраниц -- это структура, отображающая физическую страницу памяти. Они создаются при старте системы, и дальше могут быть отданы объектам. странца может принадлежать только одному обхекту.
Объект является своеобразным кешом страниц. Он гарантирует что может отобразить память начиная от и заканчивая там. Но каких-то страниц может не быть в наличии, тогда идет обращение к пейджеру.
Самый простой объект -- анонимный, за ним ничего не стоит.
у него есть дефолт пейджер -- пустой пейджер, который не умеет ни сбрасывать страницы, не аллоцировать.
Когда системе становится плохо и ей хочется свопится, все анонимные пейджи записываются в своп пейджи.
Более сложный обжект vnode object. Есть еще другие объекты. Внод_обжект за собой скрывает файл. Если файл открывался, то в ядре для него есть vnode.
Когда мы делаем ммап любая страница из фацла будет вычитываться и кластся в память.
Сколько бы вы программ не открыли, они все будут обращаться к одному объекту.
После форка у потомка будет свое адресное пространство, но будет приходить в тот же вно обжект. Но как только процесс совершит запись, то создастся shadow обжект. В топе мы видимо сколько бы съел процесс, если бы он был совсем один.
Шэдоу обжекты порождают кучу неконсистентности. Ни в одной современной ос он не реализован.
Было бы здорово эту штуку оптимизировать.
Делаются локальные оптимизации.
Сам по себе маллок, который в файле кернел.с он очень простой и очень тонкий, всю работу за него выполняет другой слой. См блоксхему. Сначала смотрим, защищенла ли память мемгардами, если используем, то уходим в пециальный модуль. Аналогично в конце можем уйти в редзон.
В нормальном ядре все проще -- если размер больше пейджсайз, то вызываем uma_large_malloc
Если меньше, то ищем ближайшую степень двойки, и аллоцируем из тма_зон.
Чтоже такое ума?
Ума это зонный аллокатор. Непосредственно те зоны.
Плиточный аллокатор, который нарезает очередную арены на кусочки и начинает ихз возвращать. Берет у ядра страницу, режет на кусочки. Держит информацию аллоцированы или нет храня в конце страницы небольшую структуру с описанием.
Иногда может уносить эту структуру в отдельный слаб, но это хуже с точки зрения процессорного кэша.
В первую очередь зона представляет собой кег. Три списка -- список полных слабов, частично пустых, пустых. Если все слабы полны, то ума обращается к вм и просит еще одну страницу.
Зона реализует кэши.
Число кэшей равно числу процессоров. Чтобы из слаба вынуть кусок памяти надо провести несколько операций, которые требуют синзронизации. Поэтому делается кэш для каждого процессора и когда мы делаем аллокацию ума, то ума входит в критическую секцию и смотрит есть ли память непосредственно в кэше этого процессора.Выигрыш двойной 00 сама аллокация быстрая, во вторых эта память была недавно освобждена этим процессором и ее сождрежимое с некоторой вероятностью уже в процессорном кэше.
Кэш представляет собой очень простуй структуру -- два ведра -- пустое и полное. Как только одно из них заполняется или доходит до нуля, мы их меняем местами.
Ведро из себя представляет сколько на максимум записей, сколько текцщее количество, и вектор из указателей в глубину слаба, и обеспечивают быструю аллокацию.
Когда у кега заканчиваются страницы, он их просто берет у вм.
Когда вмпейджи аллоцируются, то они помечаются цветами -- это те страницы, которые гарантированно будут попадать в разные линии кэша. Когда процесс требует очередную страницу то вм старается выдать ему следующий цвет после того цвета, которые он брал последним, а если такого нет, то случайный. Если пропустили один цвет, то тоже какая-то оптимизация может получиться.
Есть еще аллокатор из нетбсд vmcm(9) Универсальный аллокатор. Ему дается ресурс, он с ним может работать. В ядре фрибсд он используется для вммпа самого ядра. Потому что у ядра линейный список ентри уже неудобен.
Проуессы делают ммап доволно редко. Поэтому линейный список из вммап ентри вполне удовлетворителен.
Curt Schimel systems for modern architectures