Единственный параметр функции — это желаемое относительное время, выраженное в количестве импульсов системного таймера. В этом примере задание переводится в прерываемое состояние ожидания, которое будет длиться s секунд. Поскольку задание отмечено как TASK_INTERRUPTIBLE
, то оно может быть возвращено к выполнению раньше времени, как только оно получит сигнал. Если не нужно, чтобы код обрабатывал сигналы, то можно использовать состояние TASK_UNINTERRUPTIBLE
. Перед вызовом функции schedule_timeout()
задание должно быть в одном из этих двух состояний, иначе задание в состояние ожидания переведено не будет.
Следует обратить внимание, что поскольку функция schedule_timeout()
использует планировщик, то код, который ее вызывает, должен быть совместим с состоянием ожидания. Обсуждение, посвященное атомарности и переходу в состояние ожидания, приведено в главах 8 и 9. Если коротко, то эту функцию необходимо вызывать в контексте процесса и не удерживать при этом блокировку.
Функция schedule_timeout()
достаточно проста. Она просто использует таймеры ядра. Рассмотрим эту функцию подробнее.
signed long schedule_timeout(signed long timeout) {
timer_t timer;
unsigned long expire;
switch (timeout) {
case MAX_SCHEDULE_TIMEOUT:
schedule();
goto out;
default:
if (timeout < 0) {
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx from %p\n", timeout, builtin_return_address(0));
current->state = TASK_RUNNING;
goto out;
}
}
expire = timeout + jiffies;
init_timer(&timer);
timer.expires = expire;
timer.data = (unsigned long) current;
timer.function = process_timeout;
add_timer(&timer);
schedule();
del_timer_sync(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
Эта функция создает таймер timer
и устанавливает время срабатывания в значение timeout
импульсов системного таймера в будущем. В качестве обработчика таймера устанавливается функция process_timeout()
, которая вызывается, когда истекает период времени таймера. Далее таймер активизируется, и вызывается функция schedule()
. Так как предполагается, что текущее задание находится в состоянии TASK_INTERRUPTIBLE
или TASK_UNINTERRUPTIBLE
, то планировщик не будет выполнять текущее задание, а выберет для выполнения другой процесс.
Когда интервал времени таймера истекает, то вызывается функция process_timeout()
, которая имеет следующий вид.
void process_timeout(unsigned long data) {
wake_up_process((task_t*)data);
}
Эта функция устанавливает задание в состояние TASK_RUNNING
и помещает его в очередь выполнения.
Когда задание снова планируется на выполнение, то оно возвращается в функцию schedule_timeout()
(сразу после вызова функции schedule()
). Если задание возвращается к выполнению преждевременно, то таймер ликвидируется. После этого задание возвращается из функции ожидания по тайм-ауту.
Код оператора switch()
служит для обработки специальных случаев и не является основной частью функции. Проверка на значение MAX_SCHEDULE_TIMEOUT
позволяет заданию находиться в состоянии ожидания неопределенное время. В этом случае таймер не устанавливается (поскольку нет ограничений на интервал времени ожидания), и сразу же активизируется планировщик. Если вы это применяете, то, наверное, у вас есть лучший способ вернуть задание в состояние выполнения!
В главе 4 рассматривалось, как контекст процесса в ядре может поместить себя в очередь ожидания для того, чтобы ждать наступления некоторого события, а затем вызвать планировщик, который выберет новое задание для выполнения. Если где-то в другом месте произойдет указанное событие, то вызывается функция wake_up()
для всех заданий, которые ожидают в очереди. Эти задания возвращаются к выполнению и могут продолжать работу.