Монитор процессов и потоков

Цель работы – практическое знакомство с методикой использования функций для получения информации о процессах, потоках, модулях и кучах ОС Windows XP.

1. КРАТКИЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ

1.1 Получение списка процессов, выполняющихся в системе

Задача получения списка выполняющихся в системе процессов является одной из основных при выполнении мониторинга ресурсов, как отдельного ПК, так и ЛВС в целом, поэтому для ее решения разработано значительное количество утилит (см. лабораторные работу 1), имеется встроенное системное средство – диспетчер задач.

Все перечисленные программные средства используют как функции Win32 API (Application Program Interface – прикладной программный интерфейс), так и функции еще одного базового интерфейса, называемого Native API (естественный API). Внешняя часть Native API пользовательского режима содержится в модуле ntdll.dll, «настоящий» интерфейс реализован в ntoskernel.exe – ядре операционной системы NT (NT operation system kernel). Функции Win32 API, как правило, обращаются к функциям Native API, отбрасывая часть полученной от них информации. Поэтому использование функций Native API позволяет получить, вообще говоря, более эффективное ПО.

К функциям Win32 API для получения информации о выполняющихся в системе процессах относятся функции CreateToolhelp32Snapshot(), Process32First(), Process32Next(), Thread32First (), Thread32Next(), Module32First(), Module32Next(), Heap32ListFirst(), Heap32ListNext() и некоторые другие. Самая известная из функций Native API для доступа к содержимому многих важных внутренних структур операционной системы, таких как списки процессов, потоков, дескрипторов, драйверов и т. п. – функция NtQuerySystemInformation ().

1.1.1 Использование функций CreateToolHelp32Snapshot () и Process32xxxx() для получения списка имен процессов

Первый этап получения информации о выполняющихся в системе процессах - получение снимка (snapshot) системы, который содержит информацию о состоянии системы в момент выполнения снимка.

Снимок создается с помощью функции CreateToolhelp32Snapshot (dwFlags, th32ProcessID), первый аргумент определяет, какая информация будет записана в снимок - возможные значения dwFlags приведены в таблице.

Таблица 1

Флаг - dwFlags Описание
TH32CS_SNAPHEAPLIST В снимок включается информация о динамически распределяемой памяти указанного процесса
TH32CS_SNAPPROCESS В снимок включается список процессов, присутствующих в системе
TH32CS_SNAPTHREAD В снимок включается список потоков
TH32CS_SNAPMODULE В снимок включается список модулей, принадлежащих указанному процессу
TH32CS_SNAPALL В снимок включается список куч, процессов, потоков и модулей

Второй аргумент определяет процесс (указанием его идентификатора), информация о котором необходима (если требуется список куч или модулей). В остальных случаях он игнорируется.

Второй этап - извлечение из снимка списка процессов. Для выполнения этой операции служат функции:

BOOL Process32First (HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

BOOL Process32Next (HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

Первый аргумент - хэндл созданного снимка (возвращает функция CreateToolhelp32Snapshot).

Второй аргумент- структура, содержащая 10 полей:

1. Первое поле этой структуры - dwSize - должно перед вызовом функции содержать размер структуры в байтах - sizeof (PROCESSENTRY32).

2. Второе поле - cntUsage - содержит число ссылок на процесс, то есть число потоков, которые в настоящий момент используют какие-либо данные процесса.

3. Третье поле - th32ProcessID - является идентификатором процесса.

4. Шестое поле - cntThreads - определяет число потоков, принадлежащих процессу.

5. Седьмое поле - th32ParentProcessID - является идентификатором родительского по отношению к текущему процесса.

6. Поле pcPriClassBase cодержит базовый приоритет процесса.

7. Поле szExeFile[MAX_PATH] cодержит имя файла, создавшего процесс.

Для того, чтобы получить информацию о первом процессе в снимке, необходимо вызвать функцию Process32First. В случае успешного завершения функция возвращает TRUE. Для того, чтобы просмотреть все оставшиеся процессы, нужно вызывать функцию Process32Next до тех пор, пока она не возвратит FALSE.

Пример 1. Получить список имен выполняющихся в системе процессов, используя рассмотренные выше функции.

На форме разместить компоненты ListBox, Label и Button. В список директив проекта добавить директиву

#include <tlhelp32.h>

Обработчик события OnClick кнопки должен иметь вид:

// получение снимка процессов

HANDLE HS = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

PROCESSENTRY32 P;

ListBox1->Clear();

// задать размер структуры

P.dwSize = sizeof(PROCESSENTRY32);

// получение имен процессов и их запись в ListBox1

if (Process32First(HS, &P))

{

do

{

ListBox1->Items->Add (P.szExeFile);

}

while (Process32Next (HS, &P));

}

// освобождение ресурса – снимка состояния системы

CloseHandle (HS);

Результат выполнения примера 1 показан на рис. 1.

Рис. 1. Список выполняющихся процессов

1.1.2 Использование функций CreateToolhelp32Snapshot () и Thread32xxxx() для получения сведений о приоритетах потоков процессов

Для получения сведений о приоритетах потоков необходимо извлечь из снимка состояния системы с помощью функций Thread32First() и Thtead32Next () значения соответствующих полей.

Обращение к функциям имеет вид:

BOOL Thread32First (HANDLE hSnapshot, LPTHREADENTRY32 lpte);

BOOL Thtead32Next (HANDLE hSnapshot, LPTHREADENTRY32 lpte);

Первый аргумент - хэндл созданного снимка (возвращает функция CreateToolHelp32Snapshot).

Второй аргумент - структура, содержащая 7 полей:

1. Первое поле этой структуры - dwSize - должно перед вызовом функции содержать размер структуры в байтах - sizeof (THREADEntry32).

2. Поле th32OwnerProcessID содержит идентификатор процесса – владельца потока.

3. Поле tpBasePri содержит текущий приоритет потока.

4. Поле tpDeltaPri содержит разность между текущим уровнем приоритета потока и базовым уровнем, то есть тем, который присваивается при создании потока.

Пример 2. Получить список выполняющихся в системе потоков, используя рассмотренные выше функции. Вывести содержимое полей 2 и 3 структуры THREADEntry32

На форме разместить компоненты ListBox, Label и Button, в список директив проекта добавить директиву

#include <tlhelp32.h>

Обработчик события OnClick кнопки должен иметь вид:

THREADENTRY32 Th;

HANDLE Sh = CreateToolhelp32Snapshot (TH32CS_SNAPALL, 0);

Th.dwSize = sizeof (THREADENTRY32);

Thread32First(Sh,&Th);

ListBox1->Clear();

ListBox1->Items ->Add("процесс " + IntToStr(Th.th32OwnerProcessID) + " приоритет потока " +

IntToStr(Th.tpBasePri));

do

{

ListBox1->Items ->Add("процесс " + IntToStr(Th.th32OwnerProcessID) + " приоритет потока " +

IntToStr(Th.tpBasePri));

}

while (Thread32Next (Sh,&Th));

CloseHandle(Sh);

Результат выполнения приложения примера 2 показан на рис. 2.

Рисунок 2. Список потоков

1.1.3 Использование функций CreateToolHelp32Snapshot () и Module32xxxx()

для получения списка модулей

Для получения сведений о модулях, используемых одним из процессов, необходимо:

· получить снимок выполняющихся процессов;

· извлечь из снимка имена процессов и поместить их в ListBox, одновременно создать в памяти массив идентификаторов процессов;

· для выбранного процесса найти в массиве его идентификатор и, используя полученный идентификатор в качестве второго аргумента функции CreateToolHelp32Snapshot (), получить снимок списка модулей выбранного процесса;

· извлечь из полученного снимка с помощью функций Module32First() и Module32Next() значения соответствующих полей.

Обращение к функциям имеет вид:

BOOL Module32First (HANDLE hSnapshot, tagMODULEENTRY32 lpte);

BOOL Module32Next (HANDLE hSnapshot, tagMODULEENTRY32 lpte).

Первый аргумент - хэндл созданного снимка (возвращает функция CreateToolhelp32Snapshot).

Второй аргумент - структура, содержащая 10 полей:

unsigned long dwSize – размер структуры tagMODULEENTRY32

unsigned long th32ModuleID – размер модуля

unsigned long th32ProcessID - идентификатор процесса, владеющего модулем

unsigned long GlblcntUsage - счетчик глобальных пользователей модуля

unsigned long ProccntUsage - счетчик процессов - пользователей

unsigned char* modBaseAddr – базовый адрес модуля

unsigned long modBaseSize – базовый размер модуля

void * hModule - хэндл модуля

char[256] szModule – имя модуля

char[260] szExePath – путь размещения модуля.

1.2 Завершение выбранного процесса

Для завершения процесса используется функция TerminateProcess (HandleProc, ExitCode). Первый аргумент функции – описатель или хэндл процесса типа THandle – возвращается функцией, создавшей процесс, второй аргумент – код возврата типа DWord.

Значение описателя необходимо получить по идентификатору процесса с помощью функции

OpenProcess (unsigned long dwDesiredAcccess, // флаг доступа – например PROCESS_TERMINATE

int bInheritHandle, // handle inheritance flag

unsigned long dwProcessId); // идентификатор процесса

Некоторые значения первого параметра функции OpenProcess() приведены в таблице 2

Таблица 2

Вид доступа Оисание
PROCESS_ALL_ACCESS Specifies all possible access flags for the process object.
PROCESS_QUERY_INFORMATION Enables using the process handle in the GetExitCodeProcess and GetPriorityClass functions to read information from the process object.
PROCESS_SET_INFORMATION Enables using the process handle in the SetPriorityClass function to set the priority class of the process.
PROCESS_TERMINATE Enables using the process handle in the TerminateProcess function to terminate the process.

Алгоритм завершения процесса включает следующие шаги:

1. Создать список процессов, используя описанную в п.1.1.1 методику. Кроме имен процессов, сохраняемых в массиве Pr_Names, необходимо сохранять в дополнительном массиве идентификаторы процессов.

2. Получить номер (индекс) выделенного мышью имени завершаемого процесса, используя метод ListBox1.ItemIndex и соответствующий ему идентификатор процесса.

3. По идентификатору процесса получить его описатель, используя функцию OpenProcess().

4. Если описатель получен, завершить процесс, используя полученный описатель.

Для процессов с небольшими значениями ProcID – системных процессов – функция OpenProcess () не возвращает описатель, так как обычное приложения не должно иметь возможности останавливать системные процессы (службы). В то же время иногда необходимо иметь под рукой средство для удаления из системы зависшей службы.

Уровень привилегий приложения можно повысить (но для этого необходимо иметь привилегии администратора. В связи с этим обстоятельством данная часть работы не выполняется). В частности, отладчик для выполнения своих функций должен обладать самыми широкими полномочиями в отношении всех процессов системы. Изменение уровня привилегий процесса выполняется при помощи следующих действий:

1. Прежде всего, необходимо с помощью функции OpenProcessToken() из библиотеки advapi32.dll открыть токен доступа процесса.

2. Подготовить структуру TOKEN_PRIVILEGES, в которой разместить информацию о требуемом уровне привилегий.

3. Обратиться к функции AdjustTokenPrivilages().

1.3 Мониторинг процессов

Мониторинг выполняющихся в системе процессов – основа всех приложений для наблюдения за работой информационных систем и их пользователей. Для отслеживания появления в системе новых приложений или завершения выполнявшихся можно использовать два способа:

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

2. подключение к процедуре запуска и завершения процессов с помощью функции ядра PsSetCreateProcessNotifyRoutine(),описанной в Windows 2000 DDK, путем регистрации функции обратного вызова. Это не так просто, как хотелось бы, но более эффективно.

2. МЕТОДИКА ВЫПОЛНЕНИЯ

2.1. Выполнить базовые задания для всех бригад:

2.1.1 Используя компонент ListBox, построить список процессов, выполняющихся в системе.

2.1.2 Для выбранного процесса вывести сведения о его приоритете и количестве потоков, используя компонент StringGrid. Процесс выбирать с помощью мыши в списке из окна Listboxа, используя свойство ItemIndex.

2.1.3 Разработать простейший монитор имен процессов, используя первый способ п.1.3.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: