Системное программирование в среде Windows
Шрифт:
• PROCESS_ALL_ACCESS — устанавливаются все флаги доступа к процессу.
• PROCESS_TERMINATE — делает возможным завершение процесса с использованием функции TerminateProcess.
• PROCESS_QUERY_INFORMATION — разрешает использование дескриптора процесса в функциях GetExitCodeProcess и GetPriorityClass для получения информации о процессе.
bInheritHandle — позволяет указать, является ли новый дескриптор наследуемым. Параметр dwProcessID является идентификатором процесса, запрашивающего дескриптор.
Наконец, выполняющийся процесс может определить полный путь доступа к файлу исполняемого модуля, который использовался для его запуска, с помощью функций GetModuleFileName или GetModuleFileNameEx, при вызове которых значение параметра hModule должно устанавливаться равным NULL. При вызове этой функции из DLL будет возвращено имя файла DLL,
Дублирование дескрипторов
Родительскому и дочернему процессам может требоваться различный доступ к объекту, идентифицируемому дескриптором, который наследует дочерний процесс. Кроме того, процессу вместо псевдодескриптора, получаемого с помощью функции GetModuleFileName или GetModuleFileNameEx, может потребоваться реальный, наследуемый дескриптор, который мог бы использоваться дочерним процессом. Родительский процесс может обеспечить это, создав копию дескриптора с желаемыми разрешениями доступа и свойствами наследования. Функция, позволяющая создавать копии дескрипторов, имеет следующий вид:
По завершении выполнения функции указатель lphTargetHandle будет указывать на копию исходного дескриптора, hSourceHandle. hSourceHandle является дескриптором дублируемого объекта в процессе, указанном дескриптором hSourceProcessHandle, и должен иметь права доступа PROCESS_DUP_HANDLE; если указанного дескриптора в исходном процессе не существует, функция DuplicateHandle завершается ошибкой. Новый дескриптор, на который указывает указатель lphTargetHandle, является действительным в целевом процессе, hTargetProcessHandle. Обратите внимание на то, что в нашем рассмотрении фигурировали три процесса, включая вызывающий. Часто в роли вызывающего процесса выступает целевой или исходный процесс, и тогда соответствующий дескриптор получают с помощью функции GetCurrentProcess. Заметьте также, что процесс может создать дескриптор в другом процессе; если вы это делаете, то вам потребуется механизм, с помощью которого можно было бы передать в другой процесс идентификационные данные нового дескриптора.
Функция DuplicateHandle может применяться к дескрипторам любого типа.
Если действие параметра dwDesiredAccess не отменяется флагом DUPLICATE_SAME_ACCESS параметра dwOptions, то у него может быть много возможных значений (для получения более подробных сведений обратитесь к библиотеке MSDN оперативного справочного руководства).
Параметр dwOptions может содержать любую комбинацию указанных ниже двух флагов.
• DUPLICATE_CLOSE_SOURCE — вызывает закрытие исходного дескриптора.
• DUPLICATE_SAME_ACCESS — вынуждает игнорировать параметр dwDesiredAccess.
Напоминание
Ядро Windows поддерживает счетчики ссылок для всех объектов; этот счетчик представляет количество различных дескрипторов, ссылающихся на данный объект. В то же время, приложения не имеют доступа к этому счетчику. Любой объект не может быть уничтожен до тех пор, пока не будет закрыт его последний дескриптор, а счетчик ссылок не примет нулевое значение. Унаследованные и продублированные дескрипторы считаются отличными от исходных и также учитываются в счетчике ссылок. Наследуемые дескрипторы используются в программе 6.1 далее в этой главе. В то же время, дескрипторы, переданные из одного процесса в другой посредством той или иной формы механизма IPC, не считаются независимыми, и поэтому если один процесс закрывает такой дескриптор, то другие процессы использовать его не могут. Подобной методикой пользуются редко, однако в упражнении 6.2 вам предлагается передать значение унаследованного дескриптора из одного процесса в другой, используя механизм IPC.
Далее вы узнаете о том, как определить, завершено ли выполнение процесса.
Завершение и прекращение выполнения процесса
После того как процесс завершил свою работу, он, или, точнее, выполняющийся в этом процессе поток, может вызвать функцию ExitProcess, указав в качестве параметра кодом завершения (exit code):
Эта
Другой процесс может определить код завершения, вызвав функцию GetExitCodeProcess:
Процесс, идентифицируемый дескриптором hProcess, должен обладать правами доступа PROCESS_QUERY_INFORMATION (см. описание функции OpenProcess, которая нами уже обсуждалась). lpExitCode указывает на переменную типа DWORD, которая принимает значение кода завершения. Одним из ее возможных значений является STILL_ACTIVE, означающее, что данный процесс еще не завершился.
Наконец, один процесс может прекратить выполнение другого процесса, если у дескриптора завершаемого процесса имеются права доступа PROCESS_TERMINATE. При вызове функции завершения процесса указывается код завершения.
Предостережение
Прежде чем завершить выполнение процесса, убедитесь в том, что все ресурсы, которые он мог разделять с другими процессами, освобождены. В частности, должны быть корректно обработаны ресурсы синхронизации, о которых говорится в главе 8 (мьютексы, семафоры и события). В этом отношении могут оказаться полезными SEH (глава 4), а вызов функции ExitProcess может быть осуществлен из обработчика. В то же время, при вызове функции ExitProcess обработчики __finally и __except не выполняются, поэтому в идее завершения выполнения изнутри программы нет ничего хорошего. Особенно рискованно применять функцию TerminateProcess, поскольку у завершаемого процесса в этом случае отсутствует возможность выполнить свои SEH или вызвать функции DllMain связанных с ним библиотек DLL. Ограниченной альтернативой являются обработчики управляющих сигналов консоли, обеспечивающие возможность передачи сигнала одним процессом другому, который после этого может корректно организовать свое завершение.
Программа 6.3 иллюстрирует применение методики, обеспечивающей взаимодействие между процессами. В этом примере один процесс посылает другому процессу запрос завершения выполнения, получив который второй процесс сможет аккуратно завершить свою работу.
Процессы UNIX имеют свои идентификаторы, pid, которые сопоставимы с идентификаторами процессов Windows. Функция getpid аналогична функции GetCurrentProcessID, но эквивалентов функциям getppid и getgpid в Windows не находится ввиду отсутствия предков процессов и групп процессов.
И, наоборот, в UNIX отсутствуют дескрипторы процессов, и поэтому в ней нет функций, которые можно было бы сравнить с функциями GetCurrentProcess или OpenProcess.
В UNIX допускается использование дескрипторов (descriptors) открытых файлов после вызова функции exec, если для дескриптора файла не был установлен флаг close-on-exec. Это правило применимо только к дескрипторам файлов, которые, в силу вышесказанного, можно сравнить с наследуемыми дескрипторами (handles) файлов Windows.
Функция UNIX exit, которая фактически является функцией библиотеки С, аналогична функции ExitProcess; чтобы прекратить выполнение другого процесса ему следует послать сигнал SIGKILL.
Ожидание завершения процесса
Простейшим, но наряду с этим и обладающим наиболее ограниченными возможностями, методом синхронизации с другим процессом является ожидание его завершения. Представленные ниже стандартные функции ожидания Windows обладают рядом интересных свойств.
• Функции ожидания могут работать с самыми различными типами объектов; дескрипторы процессов являются лишь самым первым из рассматриваемых нами примеров применения этих функций.
• Эти функции могут ожидать завершения одного процесса, первого из нескольких указанных процессов или всех процессов, образующих группу.