3.1.4.7 IOCTL_GET_THREAD_CONTEXT. Входные данные: структура аппаратного контекста, описатель целевого потока.
Выходные данные: структура аппаратного контекста.
Этот IOCTL-запрос наиболее полно использует возможности API-вызова DeviceIoControl, так как здесь задействованы оба, входной и выходной, буферы. На вход поступает структура для аппаратного контекста с инициализированным полемы CONTEXT::ContextFlags, указывающим какие группы регистров аппаратного контекста должны быть возвращены в этой структуре при удачном завершении запроса. В этом проекте запрашивается весь аппаратный контекст.
3.2 Пользовательское приложение
Пользовательское приложение включает в себя два класса: CDialog и CDriver. Как понятно из названий эти классы отвечают соответственно за взаимодействие с пользователем через диалоговое окно приложения и взаимодействие с драйвером преимущественно через IOCTL-запросы.
При запуске экземпляр пользовательского приложения первым делом пытается установить драйвер, в том случае, если это не было сделано ранее другим экземпляром. Если установка вызвала ошибку, то пользователю выдаётся соответствующее сообщение, в котором в текстовом виде указывается причина её возникновения, если она была предусмотрена, иначе – просто указывается её код. Пользователь может запросить установку драйвера ещё раз, дав положительный ответ на соответствующее предложение программы. Такая процедура повторятся до тех пор, пока установка драйвера не пройдёт успешно либо пользователь откажется от повторной попытки.
После этого загружается ниспадающий список запущенных процессов, отсортированных в алфавитном порядке по своим именам, выбирается первый процесс из списка, и уже его потоки отображаются во втором ниспадающем списке. Эти списки обновляются каждый раз, когда пользователь хочет выбрать другой процесс или поток, так как для этого ему нужны последние сведения.
Далее создаётся таймер, работа которого никак не связана с работой таймера драйвера. Этот таймер отвечает за периодичный вывод полученной от драйвера информации на форму диалога.
Эта информация получается через драйвера, как уже говорилось, с помощью API-вызова DeviceIoControl:
BOOL DeviceIoControl
(HANDLE ,
DWORD ,
LPVOID , DWORD ,
LPVOID , DWORD ,
LPDWORD ,
LPOVERLAPPED );
HANDLE – описатель устройства, которому посылается запрос;
DWORD – код IOCTL-запроса;
LPVOID – адрес входного буфера;
DWORD – длина входного буфера;
LPVOID – адрес выходного буфера;
DWORD - длина выходного буфера;
LPDWORD – количество переданных байтов;
LPOVERLAPPED – структура, необходимая при использовании асинхронного выполнения запроса, чего нет в данном приложении.
Использование этого API-вызова полностью инкапсулировано в классе CDriver, в котором для выполнения каждого запроса реализован отдельный метод с именем, близким к названию IOCTL-запроса, что обеспечивает интуитивное понимание интерфейса этого класса.
Также этот класс инкапсулирует в себя использование Менеджера управления сервисами (SCM - Service Control Manager), с помощью которого осуществляется динамическая установка, запуск, останов и удаление драйвера.
4.1 Выбор операционной системы и среды программирования
В качестве операционной системы была выбрана система Widows. Это обусловлено тем, что операционная система DOS уже устарела в силу многих причин (мы уже ушли от ОС, работающих в однозадачном режиме), а других операционных систем для персональных машин с хорошим интерфейсом, действительно удобных для пользователя, еще нет. Windows по прежнему остается самой распространенной ОС для ПК. Кроме того различные среды разработки программных продуктов разработаны именно под Windows:
Visual C++, Visual Basic, Borland C++ Builder, Delphi и другие.
Языком написания пользовательской программы был выбран С++. Язык С++ дает очень богатые возможности для программистов и, пожалуй является наиболее распространенным в их среде. Это очень мощный операторный язык. Кроме того, он обеспечивает достаточную свободу в написании программ, в то время как Pascal ставит очень узкие рамки, в частности, в описании переменных и не дает возможности построения сложных операторных выражений. Языком написания драйвера был выбран С. Применение этого языка обеспечивает переносимость меджу системами: максимум, что придётся сделать – это пересобрать драйвер. В качестве среды разработки была выбрана Microsoft Visual Studio .Net, поскольку она дает мощные и удобные средства не только визуальной разработки интерфейса программного продукта, но и настройки проектов, что позволяет эффективно организовать своё рабочее место.
4.2 Интерфейс
Так выглядит окно экземпляра пользовательского приложения «Профилировщик»:
В верхней части диалога находятся два ниспадающих списка, верхний из которых отображает список запущенных процессов в системе, а нижний – список потоков этого процесса. С помощью этих элементов управления можно указать приложению, за каким процессом и каким потоком этого процесса вести наблюдение.
На диалоге есть три группы:
Группа «Информация о процессе»:
ProcessID – идентификатор процесса;
ParentID – идентификатор процесса-родителя;
BasePriority – базовый приоритет по-умолчанию для потоков процесса;
ThreadCount – количество потоков процесса;
KernelTime – суммарное время, проведённое в режиме ядра потоками процесса, 1 единица равна 100 нс;
UserTime - суммарное время, проведённое в пользовательском режиме потоками процесса, 1 единица равна 100 нс.
Группа «Информация о потоке»:
ThreadID – идентификатор потока;
BasePriority – базовый приоритет потока;
Priority – приоритет потока;
ContextSwitches – количество переключений контекста, осуществлённых потоком;
KernelTime –время, проведённое в режиме ядра (1 единица равна 100 нс);
UserTime - время, проведённое в пользовательском режиме (1 единица равна 100 нс).
WaitTime – момент времени, когда поток перешёл в состояние ожидания (отсчёт ведётся от момента запуска системы).
Группа «Контекст потока»:
Здесь представлен аппаратный контекст потока. Большинство приложений ожидают ввода от пользователя. При наблюдении за потоками такого процесса можно вообще не увидеть какие-либо изменения. Поэтому для более наглядного просмотра стоит запускать задачи, требующие больших вычислительных затрат. Например, WinAmp, с помощью которого можно проигрывать музыку – тот поток, который за это отвечает, сразу виден по изменению регистров общего назначения. Но наиболее частые изменения в регистрах различного назначения происходят в по-настоящему «тяжеловесных» задачах, к примеру, можно взять курсовой проект по Машинной графике.
4.3 Системные требования
Драйвер написан с расчётом на Windows NT версии 5.x.
Обработка запросов от несколькоих пользовательских приложений-клиентов проверена только на Windows XP Service Pack 2.
В результате работы над проектом были реализованы пользовательское приложение, взаимодействующее с Legacy-драйвером. С его помощью оно получает базовую информацию о выбранном процессе, базовую информацию и аппаратный контекст выбранного потока указанного процесса. Это приложение является базой для реализации полноценных профилировщиков приложений для трассировки целевых приложений и для обнаружения в них узких мест, что может существенно повысить эффективность труда программиста и разрабатываемого им программного обеспечения.
Список использованной литературы
1. В.П.Солдатов «Программирование драйверов Windows». Изд. 3-е, перераб. и доп. — М.: ООО «Бином-Пресс», 2006 г. — 576 с.: ил.
2. М.Руссинович, Д.Соломон «Внутреннее устройство Microsoft Windows: Windows Server 2003, Windows XP и Windows 2000», 4-е издание.
3. Дж.Рихтер «Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной версии Windows»/Пер, англ - 4-е изд. - СПб; Питер; М.: Издательско-торговый дом "Русская Редакция", 2001.
4. Schreiber, Sven B., 1958-Undocumented Windows 2000 secrets: a programmer's cookbook.
5. Garry Nebbett, Windows NT/2000 Native API.