Обработчик прерываний таймера
Теперь, когда мы разобрались, что такое jiffies
и HZ
, а также какова роль системного таймера, рассмотрим реализацию обработчика прерываний системного таймера. Обработчик прерываний таймера разбит на две части: часть, зависимую от аппаратной платформы, и независимую часть.
Подпрограмма, которая зависит от аппаратной платформы, регистрируется в качестве обработчика прерываний системного таймера и выполняется, когда срабатывает системный таймер. Конкретная работа, конечно, зависит от аппаратной платформы, но большинство обработчиков выполняют следующие действия.
• Захватывается блокировка xtime_lock
, которая защищает доступ к переменной jiffies_64
и значению текущего времени— переменной xtime
.
• Считывается или сбрасывается состояние системного таймера, если это необходимо.
• Периодически записывается новое значение абсолютного времени в часы реального времени.
• Вызывается аппаратно-независимая подпрограмма таймера do_timer()
.
Аппаратно-независимая функция do_timer()
выполняет значительно больше действий.
• Увеличивается значение переменной jiffies_64
на единицу (это безопасная операция даже для 32-разрядных аппаратных платформ, так как блокировка xtime_lock
была захвачена раньше).
• Обновляется статистка использования системных ресурсов, таких как затраченное процессорное время в режиме пользователя и в режиме ядра, для процесса, который в данный момент выполняется.
• Выполняются обработчики динамических таймеров, для которых истек период времени ожидания (это будет рассмотрено в следующем разделе).
• Вызывается функция scheduler_tick()
, как было рассмотрено в главе 4.
• Обновляется значение абсолютного времени, которое хранится в переменной xtime
.
• Вычисляются значения печально известной средней загруженности системы (load average).
Сама по себе подпрограмма очень проста, так как большинство рассмотренных действий выполняются другими функциями.
void do_timer(struct pt_regs *regs) {
jiffies_64++;
update_process_times(user_mode(regs));
update_times();
}
Макрос user_mode()
просматривает состояние регистров процессора, regs
, и возвращает значение 1, если прерывание таймера возникло в пространстве пользователя, и значение 0 — если в пространстве ядра. Это позволяет функции update_process_times()
учесть, что за время между предыдущим и данным импульсами системного таймера процесс выполнялся в режиме задачи или в режиме ядра.
void update_process_times(int user_tick) {
struct task_struct *p = current;
int cpu = smp_processor_id();
int system = user_tick ^ 1;
update_one_process(p, user_tick, system, cpu);
run_local_timers();
scheduler_tick(user_tick, system);
}
Функция update_process()
собственно обновляет значения параметров времени выполнения процесса. Эта функция тщательно продумана. Следует обратить внимание, каким образом с помощью операции исключающее ИЛИ (XOR) достигается, что одна из переменных user_tick
и system
имеет значение, равное нулю, а другая— единице. Поэтому в функции update_one_process()
можно просто прибавить необходимое значение к соответствующим счетчикам без использования оператора ветвления.
/*
* увеличиваем значения соответствующего
* счетчика импульсов таймера на единицу
*/
p->utime += user;
p->stime += system;
Необходимое значение увеличивается на 1, а другое остается без изменений. Легко заметить, что в таком случае предполагается, что за время импульса системного таймера процесс выполнялся в том же режиме, в котором он выполняется во время прихода прерывания. На самом деле процесс мог несколько раз переходить в режим задачи и в режим ядра за последний период системного таймера. Кроме того, текущий процесс может оказаться не единственным процессом, который выполнялся за последний период системного таймера. К сожалению, без применения более сложной системы учета, такой способ является лучшим из всех тех, которые предоставляет ядро. Это также одна из причин увеличения частоты системного таймера.
Далее функция run_local_timers()
помечает отложенные прерывания, как готовые к выполнению (см. главу 7, "Обработка нижних половин и отложенные действия"), для выполнения всех таймеров, для которых закончился период времени ожидания. Таймеры будут рассмотрены ниже, в разделе "Таймеры".
Наконец, функция schedule_tick()
уменьшает значение кванта времени для текущего выполняющегося процесса и устанавливает флаг need_resched
при необходимости. Для SMP-машин в этой функции также при необходимости выполняется балансировка очередей выполнения. Все это обсуждалось в главе 4.
Читать дальше