Как компилятор выкладывает код в память

хорошо, у меня есть немного вопрос студента noob.

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

но как все это работает на менее теоретическом уровне?

компилятор просто предполагает, что у него есть целая область памяти для себя от адреса 0 до адреса бесконечности? А потом просто начинайте назначать вещи?

и где он раскладывает инструкции, стек и кучу? В верхней части области памяти, в конце области памяти?

и как это работает с виртуальной памятью? Виртуальная память прозрачна для программы?

извините за вопросы bajilion, но я беру структуры языка программирования, и он продолжает ссылаться на эти регионы, и я хочу понять их на более практическом уровне.

большое спасибо заранее!

3 ответов


всеобъемлющее объяснение, вероятно, выходит за рамки этого форума. Этому вопросу посвящены целые тексты. Однако на упрощенном уровне вы можете посмотреть на это так.

компилятор не выкладывает код в память. Он предполагает, что вся область памяти в его распоряжении. Компилятор создает объектные файлы, где символы в объектных файлах обычно начинаются со смещения 0.

компоновщик отвечает за объединение объектных файлов, связывание символов с их новым местоположением смещения в связанном объекте и создание формата исполняемого файла.

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

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

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

Виртуальная память обычно колеблется от адреса 0 и до максимального адреса, поддерживаемых платформой (не бесконечность). Это виртуальное адресное пространство разделяется операционной системой на адресное пространство ядра и адресное пространство пользователя. Скажем, на гипотетической 32-битной ОС адреса выше 0x80000000 зарезервированы для операционной системы и адреса, ниже для использования программы. Если программа попытается получить доступ к памяти над этим разделом, она будет прервана.

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

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


это широкий открытый вопрос с большим количеством тем.

предполагая типичный компилятор - > ассемблер - > компоновщик toolchain. Компилятор не знает много, он просто кодирует относительные вещи стека, не заботится о том, сколько или где находится стек, то есть цель/красота стека, не заботится. Компилятор генерирует ассемблер ассемблер собирается в объект, затем компоновщик принимает сценарий компоновщика info некоторых аргументов вкуса или командной строки, которые сообщают ему подробности пространства памяти, когда вы

gcc hello.c -o hello

ваша установка binutils имеет сценарий компоновщика по умолчанию, который адаптирован к вашей цели (windows, mac, linux, независимо от того, на чем вы работаете). И этот скрипт содержит информацию о том, где начинается пространство программы, а затем оттуда он знает, где начать кучу (после текста, данных и bss). Указатель стека, скорее всего, установлен этим сценарием компоновщика и/или ОС управляет им каким-то другим способом. И это определяет ваше стек.

для операционной системы с MMU, что и Windows и Linux и Mac и BSD настольных компьютеров или ноутбуков, то да каждая программа составлена в предположении, что она имеет свое собственное адресное пространство, начиная от 0x0000 это не значит, что программа связана с начать работать на от 0x0000, это зависит от версии операционной системы, как для какой то операционной системы правил, некоторые в биты 0x8000 например.

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

виртуальная память невидима для всего этого приложение обычно не делает знать или заботиться о виртуальной памяти. если и когда приложение получает инструкцию или выполняет передачу данных, оно проходит через аппаратное обеспечение, настроенное операционной системой и преобразующее между виртуальным и физическим. Если mmu указывает на ошибку, означающую, что пространство не было сопоставлено с физическим адресом, это иногда может быть преднамеренным, а затем применяется другое использование термина "виртуальная память". Это второе определение операционная система может затем, например, взять некоторые другие кусок памяти, твой или чужой, переместите его на жесткий диск, например, отметить, что другой кусок, как не был там, а потом отметьте свой кусок как некоторые RAM, а затем позволить вам выполнить не зная, что вы были прерваны с ОЗУ, что у вас не знаю, вы должны были взять у кого-то другого. Ваше приложение по дизайну не хочет знать ничего из этого, оно просто хочет работать, операционная система заботится об управлении физической памятью и mmu, который дает вам виртуальный (нулевой) адрес пространство...

Если вы должны были сделать немного программирования голого металла, без материала mmu сначала, а затем позже с микроконтроллером, qemu, raspberry pi, beaglebone и т. д., Вы можете испачкать руки как с компилятором, скриптом компоновщика, так и с настройкой mmu. Я бы использовал arm или mips для этого не x86, просто чтобы сделать вашу жизнь проще, общая картина все переводится непосредственно через цели.


Это зависит.

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

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

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

последнее означает, что ОС накладывает некоторые правила. Например. ОС очень хотелось бы знать, где находится первая инструкция вашей программы. Простое правило может заключаться в том, что ваша программа всегда запускается по адресу 0, поэтому компилятор C может поставить int main() там. ОС обычно хотела бы знать, где стек, но это уже более гибкое правило. Что касается" кучи", ос действительно не могла заботиться.