Задания по планированию потоков
Задание 7. Исследовать очередь потоков для выполнения.
Указания к выполнению.
1. Скачайте утилиту CPUSTRES от Sysinternals по адресу: http://live.sysinternals.com/WindowsInternals/.
2. В виртуальной машине запустите утилиту CPUSTRES.
Выберите значения полей утилиты так, как показано на рисунке:
Важно обеспечить загрузку системы несколькими потоками, желательно с разными приоритетами.
3. Прервите выполнение виртуальной машины в отладчике (Ctrl+Break).
4. Определите адрес структуры KPRCB (см. предыдущее задание). Предположим, адрес структуры KPRCB равен FFDFF120.
5. Выведите на экран значения полей структуры KPRCB при помощи команды:
dt nt!_kprcb FFDFF120
6. Найдите поля ReadySummary и DispatcherReadyListHead (см. лекцию 9 "Планирование потоков", раздел "Алгоритмы планирования в Windows"):
7.
8. Поле ReadySummary показывает приоритеты, для которых имеются готовые к выполнению потоки.
Предположим, поле ReadySummary = 0x381. Переведем это значение как 32 разрядное число в двоичный вид:
Двоичные единицы в этом поле обозначают приоритеты, для которых в данный момент имеются очереди готовых потоков.
Поле ReadySummary используется для ускорения поиска очереди потоков с максимальным приоритетом: система не просматривает все очереди для каждого приоритета, а сначала обращается к полю ReadySummary, чтобы найти готовый поток с максимальным приоритетом. В данном примере это поток с приоритетом 9.
9. Поле DispatcherReadyListHead указывает на очереди готовых потоков.
Данное поле представляет собой массив элементов типа LIST_ENTRY (см. файл public\sdk\inc\ntdef.h, строка 1084). Размерность массива совпадает с количеством приоритетов в системе – 32.
Чтобы просмотреть содержимое массива, введите в отладчике следующую команду:
dd FFDFF120+9F0
Адрес получается путем прибавления смещения поля DispatcherReadyListHead (9F0) к стартовому адресу структуры KPRCB (FFDFF120).
Тип LIST_ENTRY описывает двунаправленный список и представляет собой структуру, состоящую из двух полей: Flink (Forward Link) – указатель на следующий элемент списка и Blink (Backward Link) – указатель на предыдущий элемент списка.
На рисунке показаны первые 16 элементов массива DispatcherReadyListHead и обведены разные структуры LIST_ENTRY, представляющие элементы массива и состоящие из двух адресов – Flink и Blink.
Большинство элементов массива описывают пустые списки – это ситуация, когда адреса в обоих полях структуры LIST_ENTRY совпадают и указывают на одно и то же поле – Flink. Например, на рисунке первый элемент массива представляет собой структуру LIST_ENTRY, располагающуюся по адресу FFDFFB18, оба поля которой содержат тот же самый адрес.
Нас интересуют непустые списки – это нулевой, седьмой, восьмой и девятый элементы массива (см. единичные биты в поле ReadySummary). Рассмотрим девятый элемент массива, описывающий очередь готовых потоков с максимальным в данный момент приоритетом. Заметим, что он расположен по адресу FFDFFB58.
Для девятого элемента поле Flink = 81F454C0, поле Blink = 82258570.
Посмотрим, что располагается в памяти по адресу, на который указывает Flink:
По этому адресу располагается структура LIST_ENTRY, первое поле которой (Flink) указывает на следующий элемент списка, а второе поле (Blink) – на предыдущий элемент.
Пройдем дальше, к следующему элементу списка:
Очевидно, это последний элемент списка, поскольку первое поле Flink указывает на адрес FFDFFB58 – начало списка.
Таким образом, схема расположения списка готовых потоков с приоритетом 9 будет выглядеть следующим образом:
Рассмотрим сейчас, каким образом определить потоки, на которые указывают элементы данного списка.
В списке три элемента: первый элемент по адресу FFDFFB58 является частью структуры KPRCB, второй и третий элементы представляют собой поле WaitListEntry структуры KTHREAD (см. файл base\ntos\inc\ke.h, строка 1128). Данное поле располагается по смещению 0x060 относительно начала структуры KTHREAD, это можно узнать, введя команду:
dt kthread
Таким образом, чтобы узнать адрес начала структуры KTHREAD потока в очереди готовых потоков, нужно из адреса, по которому располагается элемент списка очереди, вычесть 0x060.
Выведем на экран структуру KTHREAD для первого потока в очереди потоков. Адрес соответствующего элемента списка равен 81F454C0, поэтому используем команду:
dt kthread 81F454C0–60
Чтобы узнать процесс, к которому принадлежит данный поток, найдем поле Process структуры KTHREAD:
Обратите внимание на поле BasePriority – там указан приоритет 9, который совпадает с приоритетом очереди потоков.
В поле Process указан адрес структуры KPROCESS (поля Pcb) процесса, которому принадлежит данный поток. Поскольку поле Pcb является первым в структуре EPROCESS (нулевое смещение), то адрес структуры KPROCESS совпадает с адресом структуры EPROCESS процесса.
Выведем на экран структуру EPROCESS по найденному адресу и узнаем имя исполняемого образа по полю ImageFileName:
Как видно из рисунка, поток, находящийся первым в очереди готовых потоков с приоритетом 9, принадлежит процессу CPUSTRES.EXE. Именно этот поток в данный момент будет выбран на выполнение.
Аналогичным образом можно определить процессы-владельцы остальных потоков в очереди.
Задания для самостоятельного выполнения
Задание 1. Исследуйте функцию KeSetQuantumProcess.
Указания к выполнению.
1. Установите в отладчике точку останова на функции KeSetQuantumProcess:
bp nt!KeSetQuantumProcess
2. Измените величину кванта в системе Windows Server 2003 SP1 так, как это описано в задании 3 основной части лабораторной работы.
Задание 2. Определить базовый приоритет какого-либо потока, принадлежащего процессу explorer.exe.
Указания к выполнению.
- Базовый приоритет потока хранится в поле BasePriority, а текущий – в поле Priority структуры KTHREAD.
- Также приоритет потока отображается при выполнении команды !thread.
- Ещё один способ узнать приоритет потока – с помощью утилиты Process Explorer.
Задание 3. Изменить базовый приоритет потока.
Указания к выполнению.
1. Приоритет потока можно изменить программным путем. Например, утилита CPUSTRES от Sysinternals предоставляет возможность запустить несколько потоков с разными приоритетами.
2. Запустите утилиту CPUSTRES. Посмотрите в отладчике приоритеты её потоков. Измените приоритет потока в CPUSTRES и проверьте, что в отладчике значение также изменилось.
3. Более сложный вариант задания – написать программу, изменяющую приоритет собственных потоков. См. WinAPI функцию SetThreadPriority.
Задание 4. Исследовать функцию KiQuantumEnd.
Указания к выполнению.
1. Установите в отладчике точку останова на функции KiQuantumEnd (см. лабораторную работу 2 "Процессы и потоки", задание 4).
Для этого прервите выполнение виртуальной машины (Ctrl+Break) и воспользуйтесь следующей командой:
bp nt!KiQuantumEnd
2. Продолжите выполнение виртуальной машины (F5). Почти сразу после этого выполнение должно прерваться и управление перейдет в отладчик на функцию KiQuantumEnd (сработает точка останова).
3. Выполните трассировку функции KiQuantumEnd.