Что происходит, когда вы запускаете программу?

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

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

5 ответов

Это только на очень высоком и абстрактном уровне, конечно!

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, регистры устанавливаются внутри процессора, и волшебство происходит!

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

Хорошо, отвечаю на мой вопрос. Это будет сделано постепенно, и только для 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. программа выходит.

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

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

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

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

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

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

Другие вопросы по тегам