Когда исполняется двоичный файл, копирует ли он все свои двоичные данные в память одновременно? Могу ли я изменить это? - PullRequest
13 голосов
/ 14 декабря 2011

Копирует ли он весь двоичный файл в память перед выполнением?Меня интересует этот вопрос, и я хочу изменить его по-другому.Я имею в виду, что если двоичный файл имеет размер 100 МБ (кажется невозможным), я мог бы запустить его, пока копирую в память.Может ли это быть возможным?

Или не могли бы вы сказать мне, как увидеть, как это работает?Какие инструменты мне нужны?

Ответы [ 3 ]

32 голосов
/ 14 декабря 2011

Теоретическая модель для программиста прикладного уровня создает впечатление, что это так. Фактически, нормальный процесс запуска (по крайней мере в 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, который каждый другая программа использует, например. Как правило, это полезно только во встроенных системах, где ваша программа работает более или менее самостоятельно на компьютере.

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

6 голосов
/ 14 декабря 2011

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

Я не знаю инструмента, который действительно может показать это в реальном времени, но вы можете взглянуть на /proc/xxx/maps, где xxx - PID вашего процесса.

2 голосов
/ 14 декабря 2011

Пока вы задаете правильный вопрос, я не думаю, что вам нужно о чем-то беспокоиться.Во-первых, двоичный файл в 100M не является невозможным.Во-вторых, системный загрузчик загрузит нужные ему страницы из ELF (исполняемого и связываемого формата) в память и выполнит различные перемещения и т. Д., Которые при необходимости будут работать.Он также будет загружать все необходимые зависимости разделяемой библиотеки таким же образом.Однако это не невероятно трудоемкий процесс, и его не нужно оптимизировать.Возможно, любая «оптимизация» будет иметь значительные накладные расходы, чтобы убедиться, что она не пытается использовать что-то, что не было загружено в свое время, и, возможно, будет менее эффективным.

Если вам интересно, что отображается, как говорит fge, вы можете проверить / proc / pid / maps.Если вы хотите посмотреть, как загружается программа, вы можете попробовать запустить программу с strace, например:

strace ls

Это довольно многословно, но оно должно дать вам некоторое представление о mmap ()звонки и т. д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...