Что происходит, когда вы запускаете программу? - PullRequest
38 голосов
/ 30 июля 2009

Я хотел бы собрать здесь, что происходит, когда вы запускаете исполняемый файл в Windows, Linux и OSX. В частности, я хотел бы точно понять порядок операций: я предполагаю, что формат исполняемого файла (PE, ELF или Mach-O) загружается ядром (но я игнорирую различные разделы ELF (Исполняемый и связываемый формат) и их значение), и затем у вас есть динамический компоновщик, который разрешает ссылки, затем запускается часть __init исполняемого файла, затем основной, затем __fini, а затем программа завершена, но я уверен, что она очень грубая и, вероятно, неправильная.

Редактировать : вопрос теперь CW. Я заполняю для Linux. Если кто-то хочет сделать то же самое для Win и OSX, это было бы здорово.

Ответы [ 5 ]

33 голосов
/ 30 июля 2009

Конечно, это очень высокий и абстрактный уровень!

Executable - No Shared Libary: 

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel allocates memory from the pool to fit the binary image into
  ->Kernel loads binary into memory
  ->Kernel jumps to specific memory address
  ->Kernel starts processing the machine code located at this location
  ->If machine code has stop
  ->Kernel releases memory back to pool

Executable - Shared Library

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel allocates memory from the pool to fit the binary image into
  ->Kernel loads binary into memory
  ->Kernel jumps to specific memory address
  ->Kernel starts processing the machine code located at this location
  ->Kernel pushes current location into an execution stack
  ->Kernel jumps out of current memory to a shared memory location
  ->Kernel executes code from this shared memory location
  ->Kernel pops back the last memory location and jumps to that address
  ->If machine code has stop
  ->Kernel releases memory back to pool

JavaScript/.NET/Perl/Python/PHP/Ruby (Interpretted Languages)

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel has a hook that recognises binary images needs a JIT
  ->Kernel calls JIT
  ->JIT loads the code and jumps to a specific address
  ->JIT reads the code and compiles the instruction into the 
    machine code that the interpretter is running on
  ->Interpretture passes machine code to the kernel
  ->kernel executes the required instruction
  ->JIT then increments the program counter
  ->If code has a stop
  ->Jit releases application from its memory pool

Как говорит routeNpingme, регистры устанавливаются внутри ЦП, и происходит волшебство!

Обновление: Да, сегодня я не могу правильно написать!

25 голосов
/ 03 августа 2009

Хорошо, отвечаю на мой вопрос. Это будет сделано постепенно, и только для Linux (и, возможно, Mach-O). Не стесняйтесь добавлять больше материала к вашим личным ответам, чтобы за них проголосовали (и вы можете получить значки, так как теперь это CW).

Я начну на полпути, а остальное соберу, как узнаю. Этот документ был сделан с x86_64, gcc (GCC) 4.1.2.

Открытие файла, инициализация

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

  1. ELF открыт.
  2. ядро ​​ищет раздел .text и загружает его в память. Отмечает это как только для чтения
  3. ядро ​​загружает раздел .data
  4. ядро ​​загружает раздел .bss и инициализирует все содержимое до нуля.
  5. ядро ​​передает управление динамическому компоновщику (имя которого находится внутри файла ELF, в разделе .interp). Динамический компоновщик разрешает все вызовы общей библиотеки.
  6. контроль передается в приложение

Выполнение программы

  1. вызывается функция _start, поскольку заголовок ELF указывает ее как точку входа для исполняемого файла
  2. _start вызывает __libc_start_main в glibc (через PLT), передавая ему следующую информацию

    1. адрес актуальной основной функции
    2. адрес argc
    3. адрес argv
    4. адрес подпрограммы _init
    5. адрес процедуры _fini
    6. указатель на функцию для регистрации atexit ()
    7. самый большой из доступных стековых адресов
  3. _init вызывается

    1. вызывает call_gmon_start для инициализации профилирования gmon. на самом деле не имеет отношения к исполнению.
    2. вызывает frame_dummy, который включает __register_frame_info (адрес секции eh_frame, адрес секции bss) (FIXME: что делает эта функция? Инициализирует глобальные переменные из секции BSS, по-видимому)
    3. вызывает __do_global_ctors_aux, роль которого заключается в вызове всех глобальных конструкторов, перечисленных в разделе .ctors.
  4. главный получил название
  5. основные концы
  6. Вызывается _fini, которая, в свою очередь, вызывает __do_global_dtors_aux для запуска всех деструкторов, как указано в разделе .dtors.
  7. Программа выходит.
3 голосов
/ 15 августа 2009

В Windows сначала изображение загружается в память. Ядро анализирует, какие библиотеки (читай «DLL») ему потребуется, и загружает их тоже.

Затем он редактирует образ программы для вставки адресов памяти каждой из библиотечных функций, которые ему требуются. Эти адреса уже имеют пробел в двоичном файле .EXE, но они просто заполнены нулями.

Затем каждая процедура DLL DllMain () выполняется одна за другой от самой необходимой DLL до последней, как в порядке следования зависимостей.

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

1 голос
/ 30 июля 2009

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

0 голосов
/ 30 июля 2009

Ну, в зависимости от вашего точного определения вы должны учитывать JIT-компиляторы для таких языков, как .Net и Java. Когда вы запускаете .Net «exe», который технически не «исполняем», JIT-компилятор включается и компилирует его.

...