Здавалка
Главная | Обратная связь

Формат исполняемого файла



Исполняемый файл имеет специализированный формат – Portable Executable (PE). Такое название не случайно – этот формат используется для разных платформ. Формат PE в системе Windows является универсальным: его используют не только исполняемые файлы (*.exe), но и динамические библиотеки (*.dll) и их особые разновидности – элементы ActiveX (*.ocx) и системные драйверы (*.sys и *.drv).

PE-файл состоит из заголовка и образа исполняемой программы. Заголовок условно можно поделить на два: DOS и PE (рис. 9).

Рисунок 9 – Формат исполняемого файла

DOS-заголовок хранит ссылку (смещение) на PE-заголовок и так называемую «программу-заглушку», которая представляет собой небольшую программу DOS, выводящую простое текстовое сообщение, наподобие: «This program cannot be run in DOS mode». Это сделано для того, чтобы при ошибочной попытке запуска программы Windows под DOS она могла сообщить об ошибке.

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

Процессы

Создание процесса осуществляется вызовом функции CreateProcess:

BOOL CreateProcess(

PCTSTR applicationName,

PTSTR commandLine,

PSECURITY_ATTRIBUTES saProcess,

PSECURITY_ATTRIBUTES saThread,

BOOL inheritHandles,

DWORD createFlags,

PVOID environment,

PCTSTR currentDir,

PSTARTUPINFO startInfo,

PPROCESS_INFORMATION procInfo);

В приведенном ниже списке перечислены основные этапы создания процесса:

1. Загрузка исполняемого модуля (*.exe) в оперативную память.

2. Создание и инициализация объекта ядра «процесс».

3. Создание первичного потока.

4. Передача управления в точку входа первичного потока и начало выполнения программы.

На первом этапе функция CreateProcess производит поиск исполняемого модуля applicationName. Если имя файла не указано, используется первая лексема командной строки commandLine (первая часть командной строки, которая заканчивается пробелом или знаком табуляции и является допустимой в качестве имени файла).

На втором этапе происходит создании объекта ядра «процесс» с начальным значением счетчика числа его пользователей, равным 1. Этот объект — не сам процесс, а компактная структура данных, через которую операционная система управляет процессом. (Объект ядра «процесс» следует рассматривать как структуру данных со статистической информацией о процессе.) Затем система создает для нового процесса виртуальное адресное пространство и загружает в него код и данные как для исполняемого файла, так и для любых DLL (если таковые требуются).

Далее система формирует объект ядра «поток» (со счетчиком, равным 1) для первичного потока нового процесса. Как и в первом случае, объект ядра «поток» предназначен для управления – это компактная структура данных, через которую система управляет потоком.

Наконец, перед самым возвратом управления, функция CreateProcess открывает объекты «процесс» и «поток» и заносит их дескрипторы, специфичные для данного процесса, в элементы hProcess и hThread структуры PROCESS_INFORMATION (ссылка на которую передается в параметре procInfo). Это означает, что счетчики этих объектов увеличиваются до 2. Поэтому если дескрипторы дочернего процесса и его первичного потока не используются, их следует закрыть (п. 3.5), иначе будет происходить утечка ресурсов до тех пор, пока будет существовать родительский процесс.

Рассмотрим жизненный пример: в разрабатываемом приложении имеется кнопка «Калькулятор», которая запускает стандартный калькулятор Windows (calc.exe). За все время работы приложения пользователь может сколько угодно раз нажать на эту кнопку. Поэтому если своевременно не закрывать дескрипторы дочернего процесса и его первичного потока, до завершения работы родительского процесса будет наблюдаться утечка ресурсов, поскольку эти объекты не будут уничтожаться даже после того, как дочерний процесс завершит свою работу. Следующий пример демонстрирует грамотное решение обозначенной проблемы:

STARTUPINFO startInfo = { sizeof(startInfo) };

PROCESS_INFORMATION procInfo;

TCHAR commandLine[] = TEXT("calc.exe");

CreateProcess(NULL, commandLine, NULL, NULL, FALSE,

0, NULL, NULL, &startInfo, &procInfo);

CloseHandle(procInfo.hProcess);

CloseHandle(procInfo.hThread);

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

Созданным объектам ядра – процессу и потоку – присваиваются уникальные (среди объектов данного типа) идентификаторы. Они облегчают определение процессов и потоков в системе. Завершая свою работу, CreateProcess заносит значения идентификаторов в элементы dwProcessId и dwThreadId структуры PROCESS_INFORMATION. Однако следует отметить важный момент – система способна повторно использовать идентификаторы процессов и потоков. Это значит, что после уничтожения процесса (потока), вновь созданный процесс (поток) может получить точно такой же идентификатор. Поэтому при обращении к объекту по его идентификатору нужно быть уверенным, что он еще существует. Самый простой способ – это не закрывать дескриптор объекта (процесса или потока) до тех пор, пока нужно хранить его идентификатор.







©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.