Издание четвертое windows ® для профессионалов создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows



Pdf просмотр
страница14/68
Дата28.11.2016
Размер3.57 Mb.
Просмотров12823
Скачиваний0
1   ...   10   11   12   13   14   15   16   17   ...   68
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
классом приоритета. Вы можете присвоить ему любое значение в пределах 0–9 (по умолчанию он равен 5). Увеличивая его значение, Вы заставляете Windows 2000 выделять потокам в процессах конкретного задания более длительный квант времени, а снижая — напротив, уменьшаете этот квант.
Допустим, у меня есть два задания с обычным (normal) классом приоритета в каждом задании — по одному процессу, а в каждом процессе — по одному потоку
(тоже с обычным приоритетом. В нормальной ситуации эти два потока обрабатывались бы процессором по принципу карусели и получали бы равные кванты процессорного времени. Но если я запишу в элемент
SchedulingClass для первого задания значение 3, система будет выделять его потокам более короткий квант процессорного времени, чем потокам второго задания.
Используя
SchedulingClass, избегайте слишком больших его значений, иначе Вы замедлите общую реакцию других заданий, процессов и потоков на какие либо события в системе. Кроме того, учтите, что все сказанное здесь относится только кВ будущих версиях Windows планировщик потоков предполагается существенно изменить, чтобы операционная система могла более гибко планировать потоки в заданиях и процессах.
И последнее ограничение, которое заслуживает отдельного упоминания, связано с флагом JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION. Он отключает для всех процессов в задании вывод диалогового окна с сообщением о необработанном исключении. Система реагирует на этот флаг вызовом
SetErrorMode с флагом SEM_NOG
PFAULTERRORBOX для каждого из процессов в задании. Процесс, в котором возникнет необрабатываемое им исключение, немедленно завершается без уведомления пользователя. Этот флаг полезен в сервисных и других пакетных заданиях. В его отсутствие один из процессов в задании мог бы вызвать исключение и не завершиться,
впустую расходуя системные ресурсы.
Помимо базовых ограничений, Вы можете устанавливать расширенные, для чего применяется структура JOBOBJECT_EXTENDED_LIMIT_INFORMATION:
typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
IO_COUNTERS IoInfo;
SIZE_T ProcessMemoryLimit;
SIZE_T JobMemoryLimit;
SIZE_T PeakProcessMemoryUsed;
SIZE_T PeakJobMemoryUsed;
} JOBOBJECT_EXTENDED_LIMIT_INFORMATION, Как видите, она включает структуру JOBOBJECT_BASIC_LIMIT_INFORMATION, являясь фактически ее надстройкой. Это несколько странная структура, потому что в ней есть элементы, не имеющие никакого отношения к определению ограничений для задания. Во первых, элемент
IoInfo зарезервирован, и Вы нив коем случае не должны обращаться к нему. О том, как узнать значение счетчика ввода вывода, я расскажу позже. Кроме того, элементы
PeakProcessMemoryUsed и PeakJobMemoryUsed предназначены только для чтения и сообщают о максимальном объеме памяти, переданной соответственно одному из процессов или всем процессам в задании.
Остальные два элемента,
ProcessMemoryLimit и JobMemoryLimit, ограничивают соответственно объем переданной памяти, который может быть использован одним из процессов или всеми процессами в задании. Чтобы задать любое из этих ограничений, укажите в элементе
LimitFlags флаг JOB_OBJECT_LIMIT_JOB_MEMORY или JOB_OB
JECT_LIMIT_PROCESS_MEMORY.

105
Г ЛАВА 5
Задания
А теперь вернемся к прочим ограничениям, которые можно налагать на задания.
Структура JOBOBJECT_BASIC_UI_RESTRICTIONS выглядит так struct _JOBOBJECT_BASIC_UI_RESTRICTIONS {
DWORD UIRestrictionsClass;
} JOBOBJECT_BASIC_UI_RESTRICTIONS, В этой структуре всего один элемент,
UIRestrictionsClass, который содержит набор битовых флагов, кратко описанных в таблице 5 2.
Флаг
Описание
JOB_OBJECT_UILIMIT_EXITWINDOWS
Запрещает выдачу команд из процессов на выход из системы, завершение ее работы,
перезагрузку или выключение компьютера через функцию
ExitWindowsEx
JOB_OBJECT_UILIMIT_READCLIPBOARD
Запрещает процессам чтение из буфера обмена
JOB_OBJECT_UILIMIT_WRITECLIPBOARD
Запрещает процессам стирание буфера обмена
JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS
Запрещает процессам изменение системных параметров через
SystemParametersInfo
JOB_OBJECT_UILIMIT_DISPLAYSETTINGS
Запрещает процессам изменение параметров экрана через
ChangeDisplaySettings
JOB_OBJECT_UILIMIT_GLOBALATOMS
Предоставляет заданию отдельную глобальную таблицу атомарного доступа (global atom table) и разрешает его процессам пользоваться только этой таблицей
JOB_OBJECT_UILIMIT_DESKTOP
Запрещает процессам создание новых рабочих столов или переключение между ними через функции
CreateDesktop или
SwitchDesktop
JOB_OBJECT_UILIMIT_HANDLES
Запрещает процессам в задании использовать объекты (например, HWND), созданные внешними по отношению к этому заданию процессами
Таблица 5-2. Битовые флаги базовых ограничений по пользовательскому интерфейсу
для объекта-задания
Последний флаг, JOB_OBJECT_UILIMIT_HANDLES, представляет особый интерес он запрещает процессам в задании обращаться к USER объектам, созданным внешними по отношению к этому заданию процессами. Так, запустив утилиту Microsoft Spy++ из задания, Вы не обнаружите никаких окон, кроме тех, которые создаст сама Spy++. На рис. 5 2 показано окно Microsoft Spy++ с двумя открытыми дочерними MDI окнами.
Заметьте, что в левой секции (Threads 1) содержится список потоков в системе. Кажется, что лишь у одного из них, 000006AC SPYXX, есть дочерние окна. А все дело в том, что я запустил Microsoft Spy++ из задания и ограничил ему права на использование описателей USER объектов. В том же окне сообщается о потоках MSDEV и EXPLO
RER, но никаких упоминаний о созданных ими окнах нет. Уверяю Вас, эти потоки наверняка создали какие нибудь окна — просто Spy++ лишена возможности их видеть. В правой секции (Windows 3) утилита Spy++ должна показывать иерархию окон на рабочем столе, нотам нет ничего, кроме одного элемента — 00000000. (Это ненастоящий элемент, но Spy++ была обязана поместить сюда хоть что нибудь.)

106
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
Обратите внимание, что такие ограничения односторонни, те. внешние процессы все равно видят USER объекты, которые созданы процессами, включенными в задание. Например, если запустить Notepad в задании, а Spy++ — вне его, последняя увидит окно Notepad, даже если для задания указан флаг JOB_OBJECT_UILIMIT_HAND
LES. Кроме того, Spy++, запущенная в отдельном задании, все равно увидит это окно, если только для ее задания не установлен флаг JOB_OBJECT_UILIMIT_HAN
DLES.
Рис. 5-2.
Microsoft Spy++ работает в задании, которому ограничен доступ к описателям
USER-объектов
Ограничение доступа к описателям USER объектов — вещь изумительная, если Вы хотите создать по настоящему безопасную песочницу, в которой будут «копаться»
процессы Вашего задания. Однако часто бывает нужно, чтобы процесс в задании взаимодействовал с внешними процессами. Одно из самых простых решений здесь использовать оконные сообщения, но, если процессам в задании доступ к описателям пользовательского интерфейса запрещен, ни один из них не сможет послать сообщение (синхронно или асинхронно) окну, созданному внешним процессом. К счастью, теперь есть функция, которая поможет решить эту проблему UserHandleGrantAccess(
HANDLE hUserObj,
HANDLE hjob,
BOOL Параметр
hUserObj идентифицирует конкретный USER объект, доступ к которому
Вы хотите предоставить или запретить процессам в задании. Это почти всегда описатель окна, но USER объектом может быть, например, рабочий стол, программная ловушка, ярлык или меню. Последние два параметра,
hjob и fGrant, указывают на задание и вид ограничения. Обратите внимание, что функция не сработает, если ее вызвать из процесса в том задании, на которое указывает
hjob, — процесс не имеет права сам себе предоставлять доступ к объекту.
И последний вид ограничений, устанавливаемых для задания, относится к защите. (Введя в действие такие ограничения, Вы не сможете их отменить) Структура выглядит так:

107
Г ЛАВА 5
Задания typedef struct _JOBOBJECT_SECURITY_LIMIT_INFORMATION {
DWORD SecurityLimitFlags;
HANDLE JobToken;
PTOKEN_GROUPS SidsToDisable;
PTOKEN_PRIVILEGES PrivilegesToDelete;
PTOKEN_GROUPS RestrictedSids;
} JOBOBJECT_SECURITY_LIMIT_INFORMATION, Ее элементы описаны в следующей таблице.
Элемент
Описание
SecurityLimitFlags
Набор флагов, которые закрывают доступ администратору, запрещают маркер неограниченного доступа, принудительно назначают заданный маркер доступа, блокируют доступ по каким либо идентификаторам защиты (security ID, SID) и отменяют указанные привилегии доступа, связываемый со всеми процессами в задании
SidsToDisable
Указывает, по каким SID не разрешается доступ
PrivilegesToDelete
Определяет привилегии, которые снимаются с маркера доступа
RestrictedSids
Задает набор SID, по которым запрещается доступ к любому защищенному объекту (deny only SIDs); этот набор добавляется к маркеру доступа
Естественно, если Вы налагаете ограничения, топотом Вам, наверное, понадобится информация о них. Для этого вызовите QueryInformationJobObject(
HANDLE hJob,
JOBOBJECTINFOCLASS JobObjectInformationClass,
PVOID pvJobObjectInformation,
DWORD cbJobObjectInformationLength,
PDWORD В эту функцию, как ив, передается описатель задания, переменная перечислимого типа JOBOBJECTINFOCLASS. Она сообщает информацию об ограничениях, адрес и размер структуры данных, инициализируемой функцией. Последний параметр,
pdwReturnLength, заполняется самой функцией и указывает, сколько байтов помещено в буфер. Если эти сведения Вас не интересуют (что обычно и бывает, передавайте в этом параметре Процесс может получить информацию о своем задании, передав при вызове
QueryInformationJobObject вместо описателя задания значение NULL. Это позволит ему выяснить установленные для него ограничения. Однако аналогичный вызов
SetInformationJobObject даст ошибку, так как процесс не имеет права самостоятельно изменять заданные для него ограничения.
Включение процесса в задание
О’кэй, с ограничениями на этом закончим. Вернемся к
StartRestrictedProcess. Установив ограничения для задания, я вызываю
CreateProcess и создаю процесс, который помещаю в это задание. Я использую здесь флаги он приводит к тому, что процесс порождается, но код пока не выполняет. Поскольку
StartRestricted
Process вызывается из процесса, внешнего по отношению к заданию, его дочерний

108
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
процесс тоже не входит в это задание. Если бы я разрешил дочернему процессу немедленно начать выполнение кода, он проигнорировал бы мою песочницу со всеми ее ограничениями. Поэтому сразу после создания дочернего процесса и перед началом его работы я должен явно включить этот процесс в только что сформированное задание, вызвав AssignProcessToJobObject(
HANDLE hJob,
HANDLE Эта функция заставляет систему рассматривать процесс, идентифицируемый параметром, как часть существующего задания, на которое указывает hJob. Обратите внимание, что
AssignProcessToJobObject позволяет включить в задание только тот процесс, который еще не относится ник одному заданию. Как только процесс стал частью какого нибудь задания, его нельзя переместить в другое задание или отпустить на волю. Кроме того, когда процесс, включенный в задание, порождает новый процесс, последний автоматически помещается в тоже задание. Однако этот порядок можно изменить.
í
Включая в
LimitFlags структуры JOBOBJECT_BASIC_LIMIT_INFORMATION флаг, Вы сообщаете системе, что новый процесс может выполняться вне задания. Потом Вы должны вызвать
CreateProcess с новым флагом CREATE_BREAKAWAY_FROM_JOB. (Если Вы сделаете это без флага в
LimitFlags, функция CreateProcess завершится с ошибкой) Такой механизм пригодится на случай, если новый процесс тоже управляет заданиями.
í
Включая в
LimitFlags структуры JOBOBJECT_BASIC_LIMIT_INFORMATION флаг, Вы тоже сообщаете системе, что новый процесс не является частью задания. Но указывать в
CreateProcess какие либо флаги на этот раз не потребуется. Данный механизм полезен для процессов,
которым ничего неизвестно об объектах заданиях.
Что касается
StartRestrictedProcess, то после вызова AssignProcessToJobObject новый процесс становится частью задания. Далее я вызываю
ResumeThread, чтобы поток нового процесса начал выполняться в рамках ограничений, установленных для задания. В этот момент я также закрываю описатель потока, поскольку он мне больше не нужен.
Завершение всех процессов в задании
Уверен, именно это Вы и будете делать чаще всего. Вначале главы я упомянуло том,
как непросто остановить сборку в Developer Studio, потому что для этого ему должны быть известны все процессы, которые успел создать его самый первый процесс. (Это очень каверзная задача. Как Developer Studio справляется с ней, я объяснял в своей колонке Вопросы и ответы по Win32» в июньском выпуске Microsoft Systems Journal за 1998 год) Подозреваю, что следующие версии Developer Studio будут использовать механизм заданий, и решать задачу, о которой мыс Вами говорили, станет гораздо легче.
Чтобы уничтожить все процессы в задании, Вы просто вызываете TerminateJobObject(
HANDLE hJob,
UINT uExitCode);

109
Г ЛАВА 5
Задания
Вызов этой функции похож на вызов
TerminateProcess для каждого процесса в задании и присвоение всем кодам завершения одного значения —
uExitCode.
Получение статистической информации о задании
Мы уже обсудили, как с помощью
QueryInformationJobObject получить информацию о текущих ограничениях, установленных для задания. Этой функцией можно пользоваться и для получения статистической информации. Например, чтобы выяснить базовые учетные сведения, вызовите ее, передав
JobObjectBasicAccountingInformation во втором параметре и адрес структуры JOBOBJECT_BASIC_ACCOUNTING_INFORMATION:
typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION {
LARGE_INTEGER TotalUserTime;
LARGE_INTEGER TotalKernelTime;
LARGE_INTEGER ThisPeriodTotalUserTime;
LARGE_INTEGER ThisPeriodTotalKernelTime;
DWORD TotalPageFaultCount;
DWORD TotalProcesses;
DWORD ActiveProcesses;
DWORD TotalTerminatedProcesses;
} JOBOBJECT_BASIC_ACCOUNTING_INFORMATION, Элементы этой структуры кратко описаны в таблице 5 3.
Элемент
Описание
TotalUserTime
Процессорное время, израсходованное процессами задания в пользовательском режиме
TotalKernelTime
Процессорное время, израсходованное процессами задания в режиме ядра
ThisPeriodTotalUserTime
То же, что
TotalUserTime, но обнуляется, когда базовые ограничения изменяются вызовом
SetInformationJobObject, а флаг не используется
ThisPeriodTotalKernelTime
То же, что
ThisPeriodTotalUserTime, но относится к процессорному времени, израсходованному в режиме ядра
TotalPageFaultCount
Общее количество ошибок страниц, вызванных процессами задания
TotalProcesses
Общее число процессов, когда либо выполнявшихся в этом задании
ActiveProcesses
Текущее количество процессов в задании
TotalTerminatedProcesses
Количество процессов, завершенных из за превышения ими отведенного лимита процессорного времени
Таблица 5-3. Элементы структуры JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
Вы можете извлечь те же сведения вместе с учетной информацией по вводу выводу, передав
JobObjectBasicAndIoAccountingInformation во втором параметре и адрес структуры JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION:
typedef struct JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION {
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION BasicInfo;
IO_COUNTERS IoInfo;
} Как видите, она просто возвращает JOBOBJECT_BASIC_ACCOUNTING_INFORMA
TION и IO_COUNTERS. Последняя структура показана наследующей странице.

110
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ struct _IO_COUNTERS {
ULONGLONG ReadOperationCount;
ULONGLONG WriteOperationCount;
ULONGLONG OtherOperationCount;
ULONGLONG ReadTransferCount;
ULONGLONG WriteTransferCount;
ULONGLONG OtherTransferCount;
} Она сообщает о числе операций чтения, записи и перемещения (а также о количестве байтов, переданных при выполнении этих операций. Данные относятся ко всем процессам в задании. Кстати, новая функция
GetProcessIoCounters позволяет получить туже информацию о процессах, не входящих нив какие задания GetProcessIoCounters(
HANDLE hProcess,
PIO_COUNTERS pIoCounters);
QueryInformationJobObject также возвращает набор идентификаторов текущих процессов в задании. Но перед этим Вы должны прикинуть, сколько их там может быть,
и выделить соответствующий блок памяти, где поместятся массив идентификаторов и структура JOBOBJECT_BASIC_PROCESS_ID_LIST:
typedef struct _JOBOBJECT_BASIC_PROCESS_ID_LIST {
DWORD NumberOfAssignedProcesses;
DWORD NumberOfProcessIdsInList;
DWORD ProcessIdList[1];
} JOBOBJECT_BASIC_PROCESS_ID_LIST, В итоге, чтобы получить набор идентификаторов текущих процессов в задании,
нужно написать примерно такой код EnumProcessIdsInJob(HANDLE hjob) {
// я исхожу из того, что количество процессов в этом задании никогда не превысит 10
#define MAX_PROCESS_IDS 10
// определяем размер блока памяти (в байтах для хранения идентификаторов и структуры cb = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST) +
(MAX_PROCESS_IDS 1) * sizeof(DWORD);
// выделяем этот блок памяти pjobpil = _alloca(cb);
// сообщаем функции, на какое максимальное число процессов рассчитана выделенная нами память pjobpil >NumberOfAssignedProcesses = MAX_PROCESS_IDS;
// запрашиваем текущий список идентификаторов процессов, JobObjectBasicProcessIdList,
pjobpil, cb, &cb);
// перечисляем идентификаторы процессов for (int x = 0; x < pjobpil >NumberOfProcessIdsInList; x++) {

111
Г ЛАВА 5
Задания
// используем pjobpil >ProcessIdList[x]...
}
// так как для выделения памяти мы вызывали _alloca,
// освобождать память нам не потребуется
}
Вот и все, что Вам удастся получить через эти функции, хотя на самом деле операционная система знает о заданиях гораздо больше. Эту информацию, которая хранится в специальных счетчиках, можно извлечь с помощью функций из библиотеки Data Helper (PDH.dll) или через модуль Performance Monitor, подключаемый к Microsoft Management Console (MMC). Рис. 5 3 иллюстрирует некоторые из доступных в системе счетчиков заданий (job object counters), а рис. 5 4 — счетчики,
относящиеся к отдельным параметрам заданий (job object details counters). Заметьте,
что в задании Jeff содержится четыре процесса calc, cmd, notepad и wordpad.
Рис. 5-3.
MMC Performance Monitor: счетчики задания
Рис. 5-4.
MMC Performance Monitor: счетчики, относящиеся к отдельным параметрам задания

112
Ч АС Т Ь I I
НАЧИНАЕМ РАБОТАТЬ
Извлечь сведения из этих счетчиков Вы сможете только для техзаданий, которым были присвоены имена при вызове
CreateJobObject. По этой причине, наверное, лучше всегда именовать задания, даже если Вы и не собираетесь ссылаться на них по именам из других процессов.
Уведомления заданий
Итак, базовые сведения об объектах заданиях я изложил. Единственное, что осталось рассмотреть, — уведомления. Допустим, Вам нужно знать, когда завершаются все процессы в задании или заканчивается все отпущенное им процессорное время. Либо выяснить, когда в задании порождается или уничтожается очередной процесс. Если такие уведомления Вас не интересуют (а во многих приложениях они и не нужны),
работать с заданиями будет очень легко — не сложнее, чем я уже рассказывал. Но если они все же понадобятся, Вам придется копнуть чуть поглубже.
Информацию о том, всели выделенное процессорное время исчерпано, получить нетрудно. Объекты задания не переходят в свободное состояние до тех пор, пока их процессы не израсходуют отведенное процессорное время. Как только оно заканчивается, система уничтожает все процессы в задании и переводит его объект в свободное состояние (signaled state). Это событие легко перехватить с помощью
WaitFor
SingleObject (или похожей функции. Кстати, потом Вы можете вернуть объект задание в состояние занято (nonsignaled state), вызвав
SetInformationJobObject и выделив ему дополнительное процессорное время.
Когда я только начинал разбираться с заданиями, мне казалось, что объект задание должен переходить в свободное состояние после завершения всех его процессов. В конце концов, прекращая свою работу, объекты процессов и потоков освобождаются тоже самое вроде бы должно происходить и с заданиями. Но Microsoft предпочла сделать по другому объект задание переходит в свободное состояние после того, как исчерпает выделенное ему время. Поскольку большинство заданий начинает свою работу с одним процессом, который существует, пока не завершатся все его дочерние процессы, Вам нужно просто следить за описателем родительского процесса он освободится, как только завершится все задание. Моя функция
StartRestricted
Process как рази демонстрирует данный прием.
Но это были лишь простейшие уведомления — более продвинутые, например о создании или разрушении процесса, получать гораздо сложнее. В частности, Вам придется создать объект ядра порт завершения ввода вывода и связать с ним объект или объекты задание. После этого нужно будет перевести один или больше потоков в режим ожидания порта завершения.
Создав порт завершения ввода вывода, Вы сопоставляете с ним задание, вызывая
SetInformationJobObject следующим образом joacp;
joacp.CompletionKey = 1;
// любое значение, уникально идентифицирующее это задание joacp.CompletionPort = hIOCP; // описатель порта завершения, принимающего уведомления, JobObjectAssociateCompletionPortInformation,
&joacp, После выполнения этого кода система начнет отслеживать задание и при возникновении событий передавать их порту завершения. (Кстати, Вы можете вызывать
QueryInformationJobObject и получать ключ завершения и описатель порта, но вряд ли

113
Г ЛАВА 5
Задания это Вам когда нибудь понадобится) Потоки следят за портом завершения ввода вывода, вызывая
GetQueuedCompletionStatus:
BOOL GetQueuedCompletionStatus(
HANDLE hIOCP,
PDWORD pNumBytesTransferred,
PULONG_PTR pCompletionKey,
POVERLAPPED *pOverlapped,
DWORD Когда эта функция возвращает уведомление о событии задания,
*pCompletionKey
содержит значение ключа завершения, заданное при вызове
SetInformationJobObject для связывания задания спортом завершения. По нему Вы узнаете, в каком из заданий возникло событие. Значение в
*pNumBytesTransferred указывает, какое именно событие произошло (таблица 5 4). В зависимости от конкретного события в
*pOverlapped
может возвращаться идентификатор процесса.
Событие
Описание
JOB_OBJECT_MSG_ACTIVE_
В задании нет работающих процессов
PROCESS_ZERO
JOB_OBJECT_MSG_END_OF_
Процессорное время, выделенное процессу, исчерпано;
PROCESS_TIME
процесс завершается, и сообщается его идентификатор
JOB_OBJECT_MSG_ACTIVE_
Была попытка превысить ограничение на число активных
PROCESS_LIMIT
процессов в задании
JOB_OBJECT_MSG_PROCESS_
Была попытка превысить ограничение на объем памяти,
MEMORY_LIMIT
которая может быть передана процессу сообщается идентификатор процесса
JOB_OBJECT_MSG_JOB_
Была попытка превысить ограничение на объем памяти,
MEMORY_LIMIT
которая может быть передана заданию сообщается идентификатор процесса
JOB_OBJECT_MSG_NEW_
В задание добавлен процесс сообщается идентификатор
PROCESS
процесса
JOB_OBJECT_MSG_EXIT_
Процесс завершен сообщается идентификатор процесса
PROCESS
JOB_OBJECT_MSG_
Процесс завершен из за необработанного им исключения;
ABNORMAL_EXIT_PROCESS
сообщается идентификатор процесса
JOB_OBJECT_MSG_END_
Процессорное время, выделенное заданию, исчерпано;
OF_JOB_TIME
процессы не завершаются, и Вы можете либо возобновить их работу, задав новый лимит повремени, либо самостоятельно завершить процессы, вызвав
TerminateJobObject



Поделитесь с Вашими друзьями:
1   ...   10   11   12   13   14   15   16   17   ...   68


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал