Главная » Ядро Linux » Обработчик прерываний таймера

0

Теперь, когда  мы разобрались, что такое  jiffie s и HZ, а также  какова  роль системного таймера, рассмотрим реализацию обработчика прерываний системного таймера.  разбит  на две части:  часть, зависимую от аппаратной платформы, и независимую часть.

Подпрограмма, которая зависит  от аппаратной платформы, регистрируется в качестве  обработчика прерываний системного таймера  и выполняется, когда срабатывает системный таймер.  Конкретная работа, конечно, зависит  от аппаратной платформы, но большинство обработчиков выполняют следующие действия.

• Захватывается блокировка xtime_lock, которая защищает доступ  к  переменной  jiffies_6 4  и  значению текущего  времени—  переменной xtirne.

• Считывается или  сбрасывается состояние системного таймера, если  это необходимо.

• Периодически записывается новое  значение абсолютного времени в часы реального  времени.

• Вызывается аппаратно-независимая  подпрограмма таймера  do_timer () . Аппаратно-независимая функция do_time r ()  выполняет значительно больше

действий.

• Увеличивается значение переменной jiffies_6 4  на  единицу   (это  безопасная операция даже для  32-разрядных аппаратных платформ, так  как блокировка xtime_lock   была захвачена  раньше).

• Обновляется статистка использования  системных ресурсов, таких  как затраченное процессорное время  в режиме  пользователя и в режиме  ядра, для процесса, который в данный момент  выполняется.

• Выполняются обработчики динамических таймеров, для которых  истек период  времени ожидания (это  будет рассмотрено в следующем разделе).

• Вызывается функция scheduler_tic k () , как было рассмотрено в главе 4.

• Обновляется значение абсолютного времени, которое  хранится в переменной xtime.

• Вычисляются значения печально известной средней  загруженности системы

(load  average).

Сама  по  себе  подпрограмма очень  проста,  так  как  большинство рассмотренных действий выполняются другими  функциями.

void do_timer(struct pt_regs *regs)

{

jiffies_64++; update_process_times(user_mode(regs)); update_times();

}

Макрос user_mod e ()   просматривает состояние  регистров процессора,  regs , и возвращает значение  1, если  прерывание таймера возникло в пространстве пользователя, и  значение 0— если  в пространстве ядра.  Это  позволяет функции update _ process_time s О   учесть,  что  за  время   между  предыдущим  и  данным  импульсами системного таймера процесс выполнялся в режиме задачи  или  в режиме ядра.

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_proces s  ()   собственно обновляет значения  параметров времени   выполнения  процесса.  Эта  функция  тщательно продумана. Следует  обратить внимание, каким образом с помощью операции исключающее ИЛИ  (XOR)  достигается, что  одна  из  переменных  user_tic k  и  syste m имеет  значение, равное нулю, а другая—  единице. Поэтому в функции  update_one_proces s ()   можно просто  прибавить  необходимое значение к соответствующим счетчикам без  использования оператора  ветвления.

/*

* увеличиваем значения соответствующего счетчика импульсов таймера на единицу

*/

p->utime += user;

p->stime += system;

Необходимое  значение  увеличивается  на  1,  а  другое  остается без  изменений . Легко  заметить,  что  в  таком  случае  предполагается,  что  за  время   импульса системного  таймера процесс выполнялся  в том же режиме, в котором он  выполняется во время  прихода  прерывания. На  самом  деле  процесс мог  несколько раз  переходить в режим  задачи  и в режим  ядра  за последний период  системного таймера. Кроме того, текущий процесс может  оказаться не  единственным процессом,  который выполнялся  за последний период   системного таймера. К  сожалению, без  применения более сложной  системы учета, такой  способ является лучшим   из  всех  тех, которые предоставляет ядро.  Это  также  одна  из  причин увеличения частоты  системного таймера.

Далее  функция  run_local_timer s ()  помечает отложенные прерывания,  как  готовые  к  выполнению (см.  главу 7, "Обработка нижних половин  и  отложенные дей-

ствия"), для выполнения всех таймеров, для которых  закончился период  времени ожидания. Таймеры будут рассмотрены ниже, в разделе  "Таймеры".

Наконец, функция schedule_tic k ()  уменьшает значение кванта  времени для текущего  выполняющегося процесса и устанавливает флаг  need_resched при  необходимости. Для SMP-машин в этой  функции также  при  необходимости выполняется балансировка очередей выполнения. Все это обсуждалось в главе 4.

После  возврата  из  функции  update_process_time s ()   вызывается функция update_time s () , которая обновляет значение абсолютного времени.

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);

}

Значение переменной tick s вычисляется как  изменение количества импульсов системного таймера  с момента последнего обновления абсолютного времени. В нормальной ситуации это значение, конечно, равно  1. В редких  случаях  прерывание таймера  может  быть  пропущено, и в таком  случае  говорят, что  импульсы таймера потеряны. Это может произойти, если прерывания запрещены в течение  длительного времени. Такая  ситуация не является нормальной и часто указывает на ошибку программного кода.  Значение переменной   wall_jiffie s   увеличивается на значение  ticks , поэтому  она  равна  значению переменной  jiffie s  в  момент  самого последнего обновления абсолютного времени. Далее  вызывается функция update _ wall_tim e ()  для того, чтобы  обновить значение переменной xtime , которая содержит значение абсолютного времени. Наконец вызывается функция calc_loa d () для  того, чтобы  обновить значение средней  загруженности системы, после  чего функция update_times ()  возвращает управление.

Функция do_time r ()  возвращается в аппаратао-зависимый обработчик прерывания, который выполняет все необходимые завершающие операции, освобождает блокировку xtirae_lock  и в конце  концов возвращает управление.

Всё это происходит каждые  1/HZ секунд, т.е.  1000 раз в секунду на машине типа

PC.

Источник: Лав,  Роберт. Разработка ядра  Linux, 2-е  издание. : Пер.  с англ.  — М.  : ООО  «И.Д.  Вильяме» 2006. — 448 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии