while (!list_empty(&cwq->worklist)) {
struct work_struct *work;
void (*f)(void*);
void *data;
work = list_entry(cwq->worklist.next, struct work_struct, entry);
f = work->func;
data = work->data;
list_del_init(cwq->worklist.next);
clear_bit(0, &work->pending);
f(data);
}
Эта функция просматривает в цикле все элементы списка отложенных действий и выполняет для каждого элемента функцию, на которую указывает поле func
соответствующей структуры workqueue_struct
. Последовательность действий следующая.
• Если список не пустой, получить следующий элемент списка.
• Получить указатель на функцию (поле func
), которую необходимо вызвать, и аргумент этой функции (поле data
).
• Удалить полученный элемент из списка и обнулить бит ожидания в структуре элемента.
• Вызвать полученную функцию.
• Повторить указанные действия.
Взаимоотношения между различными, рассмотренными в этом разделе структурами достаточно запутанные. На рис. 7.1 показана диаграмма, которая эти взаимоотношения поясняет.
Рис. 7.1. Соотношения между отложенными действиями, очередями, действий и рабочими потоками
На самом верхнем уровне находятся рабочие потоки. Может существовать несколько типов рабочих потоков. Для каждого типа рабочих потоков существует один рабочий поток для каждого процессора. Различные части ядра при необходимости могут создавать рабочие потоки. По умолчанию выполняются только рабочие потоки cpu_workqueue_struct
. Структура workqueue_struct
представляет все рабочие потоки одного типа.
Например, давайте будем считать, что в дополнение к обычному типу рабочих потоков cpu_workqueue_struct
) и четыре потока типа cpu_workqueue_struct
). Для потоков типа workqueue_struct
, а для потоков типа
На самом нижнем уровне находятся отложенные действия. Драйвер создает отложенное действие, которой должно выполниться позже. Действия представлены структурами work_struct
. Кроме других полей, эта структура содержит указатель на функцию, которая должна обработать отложенное действие. Отложенное действие отправляется на выполнение
Большинство драйверов использует существующие по умолчанию рабочие потоки, которые называются
Использование очередей отложенных действий
Использовать очереди действий просто. Сначала мы рассмотрим рабочие потоки, используемые по умолчанию, —
Первый этап — это создание самого действия, которое должно быть отложено. Для создания статической структуры на этапе компиляции необходимо использовать следующий макрос.
DECLARE_WORK(name, void (*func)(void*), void *data);
Это выражение создает структуру work_struct
с именем name
, с функцией- обработчиком func
и аргументом функции-обработчика data
.
Во время выполнения отложенное действие можно создать с помощью передачи указателя на структуру, используя следующий макрос.
INIT_WORK(struct work_struct *work, void (*func)(void*), void *data);
Этот макрос динамически инициализирует отложенное действие, на структуру которого указывает указатель work
, устанавливая функцию-обработчик func
и аргумент data
.
Прототип обработчика отложенного действия имеет следующий вид.
void work_handler(void *data);