if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
return -EFAULT;
}
return 0;
}
Если из пространства пользователя передано ненулевое значение параметра tv
, то вызывается аппаратно-зависимая функция do_gettimeofday()
. Эта функция главным образом выполняет цикл считывания переменной xtime
, который был только что рассмотрен. Аналогично, если параметр tz
не равен нулю, пользователю возвращается значение часового пояса (time zone), в котором находится операционная система. Этот параметр хранится в переменной sys_tz
. Если при копировании в пространство пользователя значения абсолютного времени или часового пояса возникли ошибки, то функция возвращает значение -EFAULT
. В случае успеха возвращается нулевое значение.
Ядро предоставляет системный вызов time()
[58], однако системный вызов gettimeofday()
полностью перекрывает его возможности. Библиотека функций языка С также предоставляет другие функции, связанные с абсолютным временем, такие как ftime()
и ctime()
.
Системный вызов settimeofday()
позволяет установить абсолютное время в указанное значение. Для того чтобы его выполнить, процесс должен иметь возможность использования CAP_SYS_TIME
.
Если не считать обновления переменной xtime
, то ядро не так часто использует абсолютное время, как пространство пользователя. Одно важное исключение— это код файловых систем, который хранят в индексах файлов значения моментов времени доступа к файлам.
Таймеры
Таймеры (timers), или, как их еще иногда называют, динамические таймеры, или таймеры ядра, необходимы для управления ходом времени в ядре. Коду ядра часто необходимо откладывать выполнение некоторых функций на более позднее время. Здесь намеренно выбрано не очень четкое понятие "позже". Назначение механизма нижних половин — это не задерживать выполнение, а не выполнять работу прямо сейчас. В связи с этим необходим инструмент, который позволяет задержать выполнение работы на некоторый интервал времени. Если этот интервал времени не очень маленький, но и не очень большой, то решение проблемы — таймеры ядра.
Таймеры очень легко использовать. Необходимо выполнить некоторые начальные действия, указать момент времени окончания ожидания, указать функцию, которая будет выполнена, когда закончится интервал времени ожидания, и активизировать таймер. Указанная функция будет выполнена, когда закончится интервал времени таймера. Таймеры не являются циклическими. Когда заканчивается интервал времени ожидания, таймер ликвидируется. Это одна из причин, почему таймеры называют динамическими[59]. Таймеры постоянно создаются и ликвидируются, на количество таймеров не существует ограничений. Использование таймеров очень популярно во всех частях ядра.
Использование таймеров
Таймеры представлены с помощью структур timer_list
, которая определена в файле <linux/timer.h>
следующим образом.
struct timer_list {
struct list_head entry; /* таймеры хранятся в связанном списке */
unsigned long expires; /* время окончание срока ожидания в
импульсах системного таймера (jiffies) */
spinlock_t lock; /* блокировка для защиты данного таймера */
void (*function)(unsigned long); /*функция-обработчик таймера */
unsigned long data; /* единственный аргумент обработчика */
struct tvec_t_base_s *base; /* внутренние данные таймера, не трогать! */
};
К счастью, использование таймеров не требует глубокого понимания назначения полей этой структуры. На самом деле, крайне не рекомендуется использовать поля этой структуры не по назначению, чтобы сохранить совместимость с возможными будущими изменениями кода. Ядро предоставляет семейство интерфейсов для работы с таймерами, чтобы упростить эту работу. Все необходимые определения находятся в файле <linux/timer.h>
. Большинство реализаций находится в файле kernel/timers
.
Первый шаг в создании таймера — это его объявление в следующем виде.
struct timer_list my_timer;
Далее должны быть инициализированы поля структуры, которые предназначены для внутреннего использования. Это делается с помощью вспомогательной функции перед вызовом любых функций, которые работают с таймером.
init_timer(&my_timer);
Далее необходимо заполнить все остальные поля структуры, например, следующим образом.
my_timer.expires = jiffies + delay; /* интервал времени таймера
закончится через delay импульсов */
my_timer.data = 0; /* в функцию-обработчик будет передан параметр,
равный нулю */
my_timer.function = my_function; /* функция, которая будет выполнена,
когда интервал времени таймера истечет */
Значение поля my_timer.expires
указывает время ожидания в импульсах системного таймера (необходимо указывать абсолютное количество импульсов). Когда текущее значение переменной jiffies
становится большим или равным значению поля my_timer.expires
, вызывается функция-обработчик my_timer.function
с параметром my_timer.data
. Как видно из описания структуры timer_list
, функция-обработчик должна соответствовать следующему прототипу.
void my_timer_function(unsigned long data);
Параметр data
позволяет регистрировать несколько таймеров с одним обработчиком и отличать таймеры с различными значениями этого параметра. Если в аргументе нет необходимости, то можно просто указать нулевое (или любое другое) значение.
Последняя операция — это активизация таймера.
add_timer(&my_timer);
И таймер запускается! Следует обратить внимание на важность значения поля expired
. Ядро выполняет обработчик, когда текущее значение счетчика импульсов системного таймера больше, чем указанное значение времени срабатывания таймера, или равно ему. Хотя ядро и гарантирует, что никакой обработчик таймера не будет выполняться до истечения срока ожидания таймера, тем не менее возможны задержки с выполнением обработчика таймера. Обычно обработчики таймеров выполняются в момент времени, близкий к моменту времени срабатывания, однако они могут быть отложены и до следующего импульса системного таймера. Следовательно, таймеры нельзя использовать для работы в жестком режиме реального времени.
Иногда может потребоваться изменить момент времени срабатывания таймера, который уже активизирован. В ядре реализована функция mod_timer()
, которая позволяет изменить момент времени срабатывания активного таймера.
mod_timer(&my_timer, jiffies + new_delay); /* установка нового времени
срабатывания */
Функция mod_timer()
позволяет также работать с таймером, который проинициализирован, но не активен. Если таймер не активен, то функция mod_timer()
активизирует его. Эта функция возвращает значение 0, если таймер был неактивным, и значение 1, если таймер был активным. В любом случае перед возвратом из функции mod_timer()
таймер будут активизирован, и его время срабатывания будет установлено в указанное значение.
58
ля некоторых аппаратных платформ функция sys_time()
не реализована, а вместо этого она эмулируется библиотекой функций языка С на основании вызова gettimeofday()
.
59
Другая причина состоит в том, что в ядрах старых версий (до 2.3) существовали статические таймеры. Такие таймеры создавались во время компиляции, а не во время выполнения. Они имели ограниченные возможности и из-за их отсутствия сейчас никто не огорчается.