Сериализация
Диспетчерские объекты
Спин-блокировки абсолютно необходимы в случаях, когда требуется синхронизация кода, работающего на повышенных уровнях IRQL. Но основное правило в NT – работать на повышенных уровнях IRQL в течение как можно более короткого времени значит, использование спин-блокировок следует по возможности избегать. Диспетчерские объекты NT – это набор механизмов синхронизации, рассчитанных на применение в основном для уровня IRQL PASSIVE_LEVEL.
Базой для любого диспетчерского объекта является структура DISPAT-CHER_HEADER (определена в ntddk.h). Общим свойством любого диспетчерского объекта является то, что в каждый момент времени такой объект находится в одном из двух состояний – сигнальном или несигнальном, а также то, что поток, ожидающий захвата диспетчерского объекта, блокирован и помещен в список ожидания, находящийся в структуре DISPATCHER_HEADER.
Блокирование потока означает его особое состояние, при котором он не занимает время процессора. Блокированный поток не будет поставлен планировщиком в очередь на исполнение до тех пор, пока не будет выведен из состояния блокирования. Это фундаментально отличает ожидание освобождения любого диспетчерского объекта от попытки захвата спин-блокировки. В последнем случае, как было сказано, поток, захватывающий спин-блокировку, "крутится" в бесконечном цикле до момента успешного захвата (отсюда и название спин-блокировка – блокировка вращения в бесконечном цикле).
Единственным отличием одного диспетчерского объекта от другого является правило, в соответствии с которым меняется состояние объекта (переход в сигнальное или несигнальное состояние). В таблице 8 перечислены диспетчерские объекты и моменты изменения их состояния.
Таблица 8. Диспетчерские объекты.
Тип Объекта | Переход в сигнальное состояние | Результат для ожидающих потоков |
---|---|---|
Мьютекс (Mutex) | Освобождение мьютекса | Освобождается один из ожидающих потоков |
Семафор (Semaphore) | Счетчик захватов становится ненулевым | Освобождается некоторое число ожидающих потоков |
Событие синхронизации (Synchronization events) | Установка события в сигнальное состояние | Освобождается один из ожидающих потоков |
Событие оповещения (Notification event) | Установка события в сигнальное состояние | Освобождаются все ожидающие потоки |
Таймер синхронизации (Synchronization timer) | Наступило время или истек интервал | Освобождается один из ожидающих потоков |
Таймер оповещения (Notification timer) | Наступило время или истек интервал | Освобождаются все ожидающие потоки |
Процесс | Завершился последний поток процесса | Освобождаются все ожидающие потоки |
Поток | Поток завершился | Освобождаются все ожидающие потоки |
Файл | Завершена операция ввода/вывода | Освобождаются все ожидающие потоки |
Диспетчерские объекты управляются Диспетчером объектов. Как и все объекты Диспетчера объектов, они могут иметь имена в пространстве имен Диспетчера объектов. С помощью этого имени различные драйвера и прикладные программы могут обращаться к соответствующему объекту. Кроме того, каждый процесс имеет таблицу описателей, связанных с конкретным объектом. Как уже говорилось, описатель в таблице описателей уникален и имеет смысл только в контексте конкретного процесса.
Однако Диспетчер объектов предоставляет функцию ObReferenceObjectByHandle(), которая дает возможность получения указателя на объект по его описателю. Эту функцию, как следует из вышесказанного, можно использовать только в контексте известного процесса (для которого создавался описатель), а полученный указатель на объект уже можно использовать в случайном контексте. Чтобы такой объект впоследствии мог быть удален, по окончании его использования должна быть вызвана функция ObDereference Object().