Теоретическая модель для программиста прикладного уровня создает впечатление, что это так. Фактически, нормальный процесс запуска (по крайней мере в Linux 1.x, я думаю, что 2.x и 3.x оптимизированы, но похожи):
- Ядро создает контекст процесса (более или менее, виртуальная машина)
- В этом контексте процесса он определяет отображение виртуальной памяти, которое отображает
с адресов RAM до начала вашего исполняемого файла
- Предполагая, что вы динамически связаны (по умолчанию / обычно), программа
ld.so
(например, /lib/ld-linux.so.2
), определенный в заголовках вашей программы, устанавливает отображение памяти для разделяемых библиотек
- Ядро выполняет
jmp
в процедуре запуска вашей программы (для программы на C это
что-то вроде crtprec80
, что вызывает main
). Поскольку он только настроил отображение и фактически не загружал какие-либо страницы (*), это вызывает сбой страницы из модуля управления памятью ЦП, который является прерыванием (исключение, сигнал) для ядра.
- Обработчик Page Fault ядра загружает какой-то раздел вашей программы, включая часть
Это вызвало ошибку страницы в ОЗУ.
- Когда ваша программа запускается, если она обращается к виртуальному адресу, который не имеет поддержки RAM
это прямо сейчас, возникнут сбои страниц, и ядро приостановит программу
Вкратце, загрузите страницу с диска, а затем верните управление программе. Это все
происходит "между инструкциями" и обычно не обнаруживается.
- Когда вы используете
malloc
/ new
, ядро создает страницы ОЗУ для чтения и записи (без файлов резервных копий дисков) и добавляет их в ваше виртуальное адресное пространство.
- Если вы выбросите Page Fault, пытаясь получить доступ к области памяти, которая не установлена в отображениях виртуальной памяти, вы получите сигнал нарушения сегментации (SIGSEGV), который обычно является фатальным.
- Когда в системе заканчивается физическое ОЗУ, страницы ОЗУ удаляются; если они являются только для чтения копиями чего-либо, уже находящегося на диске (например, исполняемый файл или общий объектный файл), они просто распределяются и перезагружаются из своего источника; если они предназначены для чтения и записи (например, память, которую вы «создали» с помощью
malloc
), они записываются в (файл подкачки = файл подкачки = раздел подкачки = виртуальная память на диске). Доступ к этим «освобожденным» страницам вызывает еще одну страницу Fault, и они перезагружаются.
Как правило, пока ваш процесс не превышает доступную оперативную память - а данные почти всегда значительно больше исполняемого файла - вы можете смело делать вид, что вы одиноки в мире, и ничего подобного не происходит. 1033 *
Итак: по сути, ядро уже запускает вашу программу во время загрузки (и может даже не загружать некоторые страницы, если вы никогда не перепрыгнете в этот код / обратитесь к этим данным).
Если ваш запуск особенно вялый, вы можете взглянуть на систему prelink
, чтобы оптимизировать загрузку совместно используемой библиотеки. Это уменьшает объем работы, которую ld.so
необходимо выполнить при запуске (между exec
вашей программы и main
вызовом, а также при первом вызове подпрограмм библиотеки).
Иногда статическое связывание может улучшить производительность программы, но при значительном расходе оперативной памяти - поскольку ваши библиотеки не являются общими, вы дублируете «свой libc
» в дополнение к общему libc
, который каждый другая программа использует, например. Как правило, это полезно только во встроенных системах, где ваша программа работает более или менее самостоятельно на компьютере.
(*) На самом деле ядро немного умнее и обычно предварительно загружает некоторые страницы.
уменьшить количество ошибок страниц, но теория остается той же, независимо от
оптимизаций