Общие сведения об объектах мьютекса

Как следует из названия, объект мьютекса — это механизм синхронизации, предназначенный для обеспечения взаимоисключающего доступа к одному ресурсу, совместно используемому набором потоков режима ядра. Только драйверы самого высокого уровня, такие как драйверы файловой системы (FSD), использующие рабочие потоки руководителей, скорее всего, будут использовать объект мьютекса.

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

Объекты мьютексов имеют встроенные функции, которые предоставляют системным потокам (только в режиме ядра) взаимоисключающий доступ к общим ресурсам на компьютерах SMP без взаимоблокировки. Ядро назначает владение мьютексом одному потоку одновременно.

Получение права владения мьютексом предотвращает доставку обычных асинхронных вызовов процедур (APC) в режиме ядра. Поток не будет вытеснен APC, если ядро не выполнит APC_LEVEL прерывание программного обеспечения для запуска специального ядра APC, например подпрограммы завершения IRP диспетчера операций ввода-вывода, которая возвращает результаты исходному инициатору запроса операции ввода-вывода.

Поток может получить владение объектом мьютекса, которым он уже владеет (рекурсивное владение), но рекурсивно приобретенный объект мьютекса не устанавливается в состояние Signaled до тех пор, пока поток полностью не освобождает свое владение. Такой поток должен явно освобождать мьютекс столько раз, сколько он приобрел владение, прежде чем другой поток сможет получить мьютекс.

Ядро никогда не разрешает потоку, владеющего мьютексом, вызывать переход в пользовательский режим без предварительного освобождения мьютекса и установки ему состояния Signaled. Если любой созданный FSD поток или созданный драйвером, которому принадлежит мьютекс, пытается вернуть управление диспетчеру ввода-вывода перед освобождением владения мьютексом, ядро выключает систему.

Любой драйвер, использующий объект мьютекса, должен вызвать KeInitializeMutex один раз, прежде чем ожидать или отпустить объект мьютекса. На следующем рисунке показано, как два системных потока могут использовать объект мьютекса.

схема, иллюстрирующая ожидание объекта мьютекса.

Как показано на предыдущем рисунке, драйвер, использующий объект мьютекса, должен предоставлять хранилище для объекта мьютекса, который должен быть резидентом. Драйвер может использовать расширение устройства созданного драйвером объекта устройства, расширение контроллера, если он использует объект контроллера, или невыгвозимый пул, выделенный драйвером.

Когда драйвер вызывает KeInitializeMutex (обычно из своей подпрограммы AddDevice ), он должен передать указатель на хранилище драйвера для объекта мьютекса, который ядро инициализирует в состояние Signaled.

После инициализации такого драйвера самого высокого уровня он может управлять взаимоисключающим доступом к общему ресурсу, как показано на предыдущем рисунке. Например, подпрограммы диспетчеризации драйвера для синхронных операций и потоков могут использовать мьютекс для защиты созданной драйвером очереди для IRP.

Так как KeInitializeMutexвсегда задает начальное состояние объекта мьютекса Signaled (как показано на предыдущем рисунке):

  1. Первоначальный вызов подпрограммы диспетчеризации к KeWaitForSingleObject с указателем мьютекса немедленно переводит текущий поток в состояние готовности, присваивает потоку право владения мьютексом и сбрасывает состояние мьютекса в Состояние Без сигнала. Как только подпрограмма отправки возобновляется, она может безопасно вставить IRP в очередь, защищенную мьютексом.

  2. Когда второй поток (другая подпрограмма диспетчеризации, подпрограмма обратного вызова рабочего потока или созданный драйвером поток) вызывает KeWaitForSingleObject с указателем Mutex , второй поток переводится в состояние ожидания.

  3. Когда подпрограмма отправки завершает постановку IRP в очередь, как описано в шаге 1, она вызывает KeReleaseMutex с указателем Mutex и значением boolean Wait , которое указывает, будет ли она вызывать KeWaitForSingleObject (или KeWaitForMutexObject) с мьютексом , как только KeReleaseMutex вернет управление.

  4. При условии, что подпрограмма диспетчеризации отпустила владение мьютексом на шаге 3 (для ожидания установлено значение FALSE), мьютекс получает состояние Signaled by KeReleaseMutex. Мьютекс в настоящее время не имеет владельца, поэтому ядро определяет, ожидает ли этот мьютекс другой поток. Если это так, ядро делает второй поток (см. шаг 2) владельцем мьютекса, возможно, повышает приоритет потока до самого низкого значения приоритета в режиме реального времени и изменяет его состояние на готово.

  5. Ядро отправляет второй поток для выполнения, как только будет доступен процессор, то есть когда ни один другой поток с более высоким приоритетом в настоящее время не находится в состоянии готовности и нет подпрограмм в режиме ядра, которые должны выполняться на более высоком IRQL. Второй поток (подпрограмма диспетчеризации, очередная очередь IRP, подпрограмма обратного вызова рабочего потока драйвера или созданный драйвером поток, выводя из очереди IRP) теперь может безопасно обращаться к защищенной мьютексом очереди IRP, пока не вызовет KeReleaseMutex.

Если поток получает право владения объектом мьютекса рекурсивно, этот поток должен явным образом вызывать KeReleaseMutex столько раз, сколько он ожидал на мьютексе, чтобы присвоить объекту мьютекса состояние Signaled. Например, если поток вызывает KeWaitForSingleObject , а затем KeWaitForMutexObject с тем же указателем Mutex , он должен дважды вызвать KeReleaseMutex при получении мьютекса, чтобы присвоить объекту мьютекса состояние Signaled.

Вызов KeReleaseMutex с параметром Wait , равным TRUE , указывает на намерение вызывающего объекта немедленно вызвать подпрограмму поддержки KeWaitXxx по возвращении из KeReleaseMutex.

Примите во внимание следующие рекомендации по настройке параметра Wait значения KeReleaseMutex:

Выгружаемый поток или подпрограмма драйвера, которая выполняется в IRQL PASSIVE_LEVEL никогда не должна вызывать KeReleaseMutex с параметром Wait , равным TRUE. Такой вызов вызывает неустранимую ошибку страницы, если вызывающий объект выходит на страницу между вызовами KeReleaseMutex и объектов KeWaitXxx.

Любая стандартная подпрограмма драйвера, которая выполняется в IRQL больше PASSIVE_LEVEL не может ждать ненулевого интервала для каких-либо объектов диспетчера без отключения системы. Однако такая подпрограмма может вызывать KeReleaseMutex , если ей принадлежит мьютекс при выполнении в IRQL меньше или равно DISPATCH_LEVEL.

Сводку по спискам IRQL, в которых выполняются стандартные подпрограммы драйвера, см. в разделе Управление приоритетами оборудования.