Subprocesos de trabajo del sistema
Un controlador que requiere procesamiento retrasado puede usar un elemento de trabajo, que contiene un puntero a una rutina de devolución de llamada del controlador que realiza el procesamiento real. El controlador pone en cola el elemento de trabajo y un subproceso de trabajo del sistema quita el elemento de trabajo de la cola y ejecuta la rutina de devolución de llamada del controlador. El sistema mantiene un grupo de estos subprocesos de trabajo del sistema, que son subprocesos del sistema que cada uno procesa un elemento de trabajo cada vez.
El controlador asocia una rutina de devolución de llamada workItem con el elemento de trabajo. Cuando el subproceso de trabajo del sistema procesa el elemento de trabajo, llama a la rutina WorkItem asociada. En Windows Vista y versiones posteriores de Windows, un controlador puede asociar en su lugar una rutina WorkItemEx con un elemento de trabajo. WorkItemEx toma parámetros diferentes de los parámetros que toma WorkItem .
Las rutinas WorkItem y WorkItemEx se ejecutan en un contexto de subproceso del sistema. Si una rutina de distribución de controladores se puede ejecutar en un contexto de subproceso en modo de usuario, esa rutina puede llamar a una rutina WorkItem o WorkItemEx para realizar cualquier operación que requiera un contexto de subproceso del sistema.
Para usar un elemento de trabajo, un controlador realiza los pasos siguientes:
Asigne e inicialice un nuevo elemento de trabajo.
El sistema usa una estructura de IO_WORKITEM para contener un elemento de trabajo. Para asignar una nueva estructura IO_WORKITEM e inicializarla como un elemento de trabajo, el controlador puede llamar a IoAllocateWorkItem. En Windows Vista y versiones posteriores de Windows, el controlador puede asignar su propia estructura de IO_WORKITEM y llamar a IoInitializeWorkItem para inicializar la estructura como un elemento de trabajo. (El controlador debe llamar a IoSizeofWorkItem para determinar el número de bytes necesarios para contener un elemento de trabajo).
Asocie una rutina de devolución de llamada al elemento de trabajo y poner en cola el elemento de trabajo para que un subproceso de trabajo del sistema lo procese.
Para asociar una rutina WorkItem con el elemento de trabajo y poner en cola el elemento de trabajo, el controlador debe llamar a IoQueueWorkItem. Para asociar en su lugar una rutina WorkItemEx con el elemento de trabajo y poner en cola el elemento de trabajo, el controlador debe llamar a IoQueueWorkItemEx.
Después de que el elemento de trabajo ya no sea necesario, liberelo.
IoFreeWorkItem debe liberar un elemento de trabajo asignado por IoAllocateWorkItem. IoInitializeWorkItem debe no inicializar un elemento de trabajo inicializado por IoUninitializeWorkItem para poder liberarlo.
El elemento de trabajo solo puede no inicializarse o liberarse cuando el elemento de trabajo no está en cola actualmente. El sistema pone en cola el elemento de trabajo antes de llamar a la rutina de devolución de llamada del elemento de trabajo, por lo que se puede llamar a IoFreeWorkItem e IoUninitializeWorkItem desde la devolución de llamada.
Un DPC que necesite iniciar una tarea de procesamiento que requiera un procesamiento largo o que realice una llamada de bloqueo debe delegar el procesamiento de esa tarea en uno o varios elementos de trabajo. Mientras se ejecuta un DPC, se impide que se ejecuten todos los subprocesos. Además, un DPC, que se ejecuta en IRQL = DISPATCH_LEVEL, no debe realizar llamadas de bloqueo. Sin embargo, el subproceso de trabajo del sistema que procesa un elemento de trabajo se ejecuta en IRQL = PASSIVE_LEVEL. Por lo tanto, el elemento de trabajo puede contener llamadas de bloqueo. Por ejemplo, un subproceso de trabajo del sistema puede esperar en un objeto distribuidor.
Dado que el grupo de subprocesos de trabajo del sistema es un recurso limitado, las rutinas WorkItem y WorkItemEx solo se pueden usar para las operaciones que tardan un breve período de tiempo. Si una de estas rutinas se ejecuta durante demasiado tiempo (si contiene un bucle indefinido, por ejemplo) o espera demasiado tiempo, el sistema puede interbloqueo. Por lo tanto, si un controlador requiere largos períodos de procesamiento retrasado, debe llamar a PsCreateSystemThread para crear su propio subproceso del sistema.
No llame a IoQueueWorkItem o IoQueueWorkItemEx para poner en cola un elemento de trabajo que ya está en la cola. Si lo hace, puede causar daños en las estructuras de datos del sistema. Si el controlador pone en cola el mismo elemento de trabajo cada vez que se ejecuta una rutina de controlador determinada, puede usar la siguiente técnica para evitar poner en cola el elemento de trabajo una segunda vez si ya está en la cola:
- El controlador mantiene una lista de tareas para la rutina de trabajo.
- Esta lista de tareas está disponible en el contexto que se proporciona a la rutina de trabajo. La rutina de trabajo y las rutinas de controlador que modifican la lista de tareas sincronizan su acceso a la lista.
- Cada vez que se ejecuta la rutina de trabajo, realiza todas las tareas de la lista y quita cada tarea de la lista a medida que se completa la tarea.
- Cuando llega una nueva tarea, el controlador agrega esta tarea a la lista. El controlador pone en cola el elemento de trabajo solo si la lista de tareas estaba vacía anteriormente.
El subproceso de trabajo del sistema quita el elemento de trabajo de la cola antes de llamar al subproceso de trabajo. Por lo tanto, un subproceso de controlador puede poner en cola de nuevo el elemento de trabajo en cuanto el subproceso de trabajo comienza a ejecutarse.