Поделиться через


Потоки системной рабочей роли

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

Драйвер связывает подпрограмму обратного вызова WorkItem с рабочим элементом. Когда системный рабочий поток обрабатывает рабочий элемент, он вызывает связанную подпрограмму WorkItem . В Windows Vista и более поздних версиях Windows драйвер может вместо этого связать подпрограмму WorkItemEx с рабочим элементом. WorkItemEx принимает параметры, отличные от параметров, которые принимает WorkItem .

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

Чтобы использовать рабочий элемент, драйвер выполняет следующие действия:

  1. Выделение и инициализация нового рабочего элемента.

    Система использует структуру IO_WORKITEM для хранения рабочего элемента. Чтобы выделить новую структуру IO_WORKITEM и инициализировать ее как рабочий элемент, драйвер может вызвать IoAllocateWorkItem. В Windows Vista и более поздних версиях Windows драйвер может также выделить собственную структуру IO_WORKITEM и вызвать IoInitializeWorkItem для инициализации структуры в качестве рабочего элемента. (Драйвер должен вызвать IoSizeofWorkItem , чтобы определить количество байтов, необходимых для хранения рабочего элемента.)

  2. Свяжите подпрограмму обратного вызова с рабочим элементом и поставить рабочий элемент в очередь, чтобы он обрабатывался системным рабочим потоком.

    Чтобы связать подпрограмму WorkItem с рабочим элементом и поместить его в очередь, драйвер должен вызвать IoQueueWorkItem. Чтобы вместо этого связать подпрограмму WorkItemEx с рабочим элементом и поместить рабочий элемент в очередь, драйвер должен вызвать IoQueueWorkItemEx.

  3. После того как рабочий элемент больше не требуется, освободите его.

    Рабочий элемент, выделенный IoAllocateWorkItem , должен быть освобожден IoFreeWorkItem. Перед освобождением рабочий элемент, инициализированный с помощью IoInitializeWorkItem , должен быть неинициализирован ioUninitializeWorkItem .

    Рабочий элемент можно не инициализировать или освободить, только если рабочий элемент в настоящее время не поставлен в очередь. Система выводит рабочий элемент из очереди перед вызовом процедуры обратного вызова рабочего элемента, поэтому из обратного вызова можно вызвать IoFreeWorkItem и IoUninitializeWorkItem .

DPC, который должен инициировать задачу обработки, требующую длительной обработки или выполняющий блокирующий вызов, должен делегировать обработку этой задачи одному или нескольким рабочим элементам. Во время выполнения DPC все потоки не выполняются. Кроме того, DPC, который выполняется в IRQL = DISPATCH_LEVEL, не должен выполнять блокирующие вызовы. Однако системный рабочий поток, обрабатывающий рабочий элемент, выполняется в irQL = PASSIVE_LEVEL. Таким образом, рабочий элемент может содержать блокирующие вызовы. Например, системный рабочий поток может ожидать объекта диспетчера.

Так как пул системных рабочих потоков является ограниченным ресурсом, подпрограммы WorkItem и WorkItemEx можно использовать только для операций, которые занимают короткое время. Если одна из этих подпрограмм выполняется слишком долго (например, если она содержит неопределенный цикл) или ожидает слишком долго, система может выполнить взаимоблокировку. Поэтому, если драйвер требует длительных периодов отложенной обработки, он должен вместо этого вызвать PsCreateSystemThread для создания собственного системного потока.

Не вызывайте IoQueueWorkItem или IoQueueWorkItemEx для постановки в очередь рабочего элемента, который уже находится в очереди. Это может привести к повреждению системных структур данных. Если драйвер помещает в очередь один и тот же рабочий элемент каждый раз при выполнении определенной подпрограммы драйвера, можно использовать следующий метод, чтобы избежать постановки рабочего элемента в очередь во второй раз, если он уже находится в очереди:

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

Системный рабочий поток удаляет рабочий элемент из очереди перед вызовом рабочего потока. Таким образом, поток драйвера может безопасно ставить рабочий элемент в очередь снова, как только рабочий поток начинает выполняться.