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.
После возврата из функции update_process_times()
вызывается функция update_times()
, которая обновляет значение абсолютного времени.
void update_times(void) {
unsigned long ticks;
ticks = jiffies - wall_jiffies;
if (ticks) {
wall_jiffies += ticks;
update_wall_time(ticks);
}
last_time_offset = 0;
calc_load(ticks);
}
Значение переменной ticks
вычисляется как изменение количества импульсов системного таймера с момента последнего обновления абсолютного времени. В нормальной ситуации это значение, конечно, равно 1. В редких случаях прерывание таймера может быть пропущено, и в таком случае говорят, что импульсы таймера потеряны. Это может произойти, если прерывания запрещены в течение длительного времени. Такая ситуация не является нормальной и часто указывает на ошибку программного кода. Значение переменной wall_jiffies
увеличивается на значение ticks
, поэтому она равна значению переменной jiffies
в момент самого последнего обновления абсолютного времени. Далее вызывается функция update_wall_time()
для того, чтобы обновить значение переменной xtime
, которая содержит значение абсолютного времени. Наконец вызывается функция calc_load()
для того, чтобы обновить значение средней загруженности системы, после чего функция update_times()
возвращает управление.
Функция do_timer()
возвращается в аппаратно-зависимый обработчик прерывания, который выполняет все необходимые завершающие операции, освобождает блокировку xtime_lock
и в конце концов возвращает управление.
Всё это происходит каждые 1/HZ
секунд, т.е. 1000 раз в секунду на машине типа PC.
Абсолютное время