Las colas de tareas pueden ser entendidas como una extensión dinámica de los viejos bottom halves. En realidad, en el código fuente son a veces referidas como los "nuevos" bottom halves. Más específicamente, los viejos bottom halves discutidos en la sección anterior tienen estas limitaciones:
- Sólo hay un número fijo de ellos (32).
- Cada bottom half sólo puede estar asociado con una función de manejador.
- Los Bottom halves son consumidos con un spinlock mantenido, por lo tanto no pueden bloquear.
Por lo tanto, con las colas de tareas, un número arbitrario de funciones pueden ser encadenadas y procesadas una después de otra en un tiempo posterior. Uno crea una nueva cola de tareas usando la macro
DECLARE_TASK_QUEUE() y encola la tarea en él usando la función
queue_task(). La cola de tareas entonces puede ser procesada usando
run_task_queue(). En vez de crear nuestra propia cola de tareas (y tener que consumirla manualmente) puedes usar una de las colas de tareas predefinidas en Linux las cuales son consumidas en puntos bien conocidos:
- tq_timer: la cola de tareas de cronómetros, funciona en cada interrupción del cronómetro y cuando se libera un dispositivo tty (cerrando o liberando un dispositivo de terminal medio abierto). Desde que el manejador de cronómetro funciona en el contexto de interrupción, la tarea tq_timer también funciona en el contexto de interrupción y de este modo tampoco puede bloquearse.
- tq_scheduler: la cola de tareas del planificador, consumida por el planificador (y también cuando se cierran dispositivos tty, como tq_timer). Como el planificador es ejecutado en el contexto de los procesos siendo re-planificados, las tareas tq_scheduler pueden hacer todo lo que quieran, esto es bloquear, usar los datos del contexto de los procesos (pero porque ellos quieren), etc .
- tq_immediate: esto es realmente un bottom half IMMEDIATE_BH, por lo tanto los controladores pueden queue_task(task, &tq_immediate) y entonces mark_bh(IMMEDIATE_BH) ser consumido en el contexto de interrupción.
- tq_disk: usado por un acceso de dispositivo de bloqueo de bajo nivel (y RAID) para empezar la actual petición. Esta cola de tareas es exportada a los módulos pero no debería de ser usada excepto para los propósitos especiales para los que fue diseñada.
A menos que un controlador use su propia cola de tareas, no necesita llamar a
run_tasks_queues() para procesar la cola, excepto bajo ciertas circunstancias explicadas a continuación.
El motivo por el que la cola de tareas
tq_timer/tq_scheduler no es consumida sólo en los sitios usuales sino en otras partes (cerrando un dispositivo tty, pero no el único ejemplo) se aclara si uno recuerda que el controlador puede planificar tareas en la cola, y estas tareas solo tienen sentido mientras una instancia particular del dispositivo sea todavía válida - lo cual usualmente significa hasta que la aplicación la cierre. Por lo tanto, el controlador quizás necesite llamar a
run_task_queue() para encender las tareas que el (y alguno más) ha puesto en la cola, porque permitiéndoles funcionar en un tiempo posterior quizás no tenga sentido - esto es, las estructuras de datos relevantes quizás no hayan sido liberadas/reusadas por una instancia diferente. Este es el motivo por el que ves
run_task_queue() en
tq_timer y
tq_scheduler en otros lugares más que el cronómetro de interrupciones y
schedule() respectivamente.