Заголовок диспетчера содержит тип объекта, информацию о состоянии (занят/свободен) и список потоков, ожидающих этот объект. У каждого ждущего потока есть список блоков ожидания, где перечислены ожидаемые потоком объекты, а у каждого объекта диспетчера ядра — список блоков ожидания, где перечислены ожидающие его потоки. Этот список ведется так, что при освобождении объекта диспетчера ядро может быстро определить, кто ожидает данный объект. B блоке ожидания имеются указатели на объект ожидания, ожидающий поток и на следующий блок ожидания (если поток ждет более одного объекта). Он также регистрирует тип ожидания («любой» или «все») и позицию соответствующего элемента в таблице описателей, переданную потоком в функцию WaitForMultipleObjects (позиция 0 — если поток ожидает лишь один объект).
Ha рис. 3-27 показана связь объектов диспетчера ядра с блоками ожидания потоков. B данном примере поток 1 ждет объект В, а поток 2 — объекты A и В. Если объект A освободится, поток 2 не сможет возобновить свое выполнение, так как ядро обнаружит, что он ждет и другой объект. C другой стороны, при освобождении объекта B ядро сразу же подготовит поток 1 к выполнению, поскольку он не ждет никакие другие объекты.
ЭКСПЕРИМЕНТ: просмотр очередей ожидания
Хотя многие утилиты просмотра процессов умеют определять, находится ли поток в состоянии ожидания (отмечая в этом случае и тип ожидания), список объектов, ожидаемых потоком, можно увидеть только с помощью команды !process отладчика ядра. Например, следующий фрагмент вывода команды !process показывает, что поток ждет на объекте-событии.
Эти данные позволяют нам убедиться в отсутствии других потоков, ожидающих данный объект, поскольку указатели начала и конца списка ожидания указывают на одно и то же место (на один блок ожидания). Копия блока ожидания (по адресу 0x8a12a398) дает следующее:
Если в списке ожидания более одного элемента, вы можете выполнить ту же команду со вторым указателем в поле WaitListEntry каждого блока ожидания (команду !thread применительно к указателю потока в блоке ожидания) для прохода по списку и просмотра других потоков, ждущих данный объект.
Быстрые и защищенные мьютексы
Быстрые мьютексы (fast mutexes), также известные как мьютексы исполнительной системы, обычно обеспечивают более высокую производительность, чем объекты «мьютекс». Почему? Дело в том, что быстрые мьютексы, хоть и построены на объектах событий диспетчера, в отсутствие конкуренции не требуют ожидания объекта «событие» (и соответственно спин-блокировок, на которых основан этот объект). Эти преимущества особенно ярко проявляются в многопроцессорной среде. Быстрые мьютексы широко используются в ядре и драйверах устройств.
Однако быстрые мьютексы годятся, только если можно отключить доставку обычных APC режима ядра. B исполнительной системе определены две функции для захвата быстрых мьютексов: ExAcquireFastMutex и ExAcquire-FastMutexUnsafe. Первая функция блокирует доставку всех APC, повышая IRQL процессора до уровня APC_LEVEL, а вторая — ожидает вызова при уже отключенной доставке обычных APC режима ядра (такое отключение возможно повышением IRQL до уровня «APC» или вызовом KeEnterCriticalRegiori). Другое ограничение быстрых мьютексов заключается в том, что их нельзя захватывать рекурсивно, как объекты «мьютекс».
Защищенные мьютексы (guarded mutexes) — новшество Windows Server 2003; они почти идентичны быстрым мьютексам (хотя внутренне используют другой синхронизирующий объект, KGATE). Захватить защищенные мьютексы можно вызовом функции KeAcquireGuardedMutex, отключающей доставку всех APC режима ядра через KeEnterGuardedRegion, а не KeEnterCritical-Region, которая на самом деле отключает только обычные APC режима ядра. Защищенные мьютексы недоступны вне ядра и используются в основном диспетчером памяти для защиты глобальных операций вроде создания страничных файлов, удаления определенных типов разделов общей памяти и расширения пула подкачиваемой памяти. (Подробнее о диспетчере памяти см. главу 7.)
Читать дальше
Конец ознакомительного отрывка
Купить книгу