содержит количество секунд, которые прошли с 1 января 1970 года (UTC, Universal Coordinated Time, всеобщее скоординированное время). Указанная дата называется epoch (начало эпохи). В большинстве Unix-подобных операционных систем счет времени ведется с начала эпохи. В поле
xtime.tv_nsec
хранится количество наносекунд, которые прошли в последней секунде.
Чтение или запись переменной
xtime
требует захвата блокировки
xtime_lock
. Это блокировка — не обычная спин-блокировка, а секвентная блокировка, которая рассматривается в главе 9, "Средства синхронизации в ядре".
Для обновления значения переменной
xtime
необходимо захватить секвентную блокировку на запись следующим образом.
write_seqlock(&xtime_lock);
/* обновить значение переменной xtime ... */
write_sequnlock(&xtime_lock);
Считывание значения переменной
xtime
требует применения функций
read_seqbegin
и
read_seqretry
следующим образом.
do {
unsigned long lost;
seq = read_seqbegin(&xtime_lock);
usec = timer->get_offset;
lost = jiffies — wall_jiffies;
if (lost)
usec += lost * (1000000 / HZ);
sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000);
} while (read_seqretry(&xtime_lock, seq));
Этот цикл повторяется до тех пор, пока не будет гарантии того, что во время считывания данных не было записи. Если во время выполнения цикла приходит прерывание таймера и переменная
xtime
обновляется во время выполнения цикла, возвращаемый номер последовательности будет неправильным и цикл повторится снова.
Главный пользовательский интерфейс для получения значения абсолютного времени — это системный вызов
gettimeofday
, который реализован как функция
sys_gettimeofday
следующим образом.
asmlinkage long sys_gettimeofday(struct timeval *tv,
struct timezone *tz) {
if (likely(tv !=NULL)) {
struct timeval_ktv;
do_gettimeofday(&ktv);
if (copy_to_userftv, &ktv, sizeof(ktv))
return -EFAULT;
}
if (unlikely(tz != NULL)) {
if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
return -EFAULT;
}
return 0;
}
Если из пространства пользователя передано ненулевое значение параметра
tv
, то вызывается аппаратно-зависимая функция
do_gettimeofday
. Эта функция главным образом выполняет цикл считывания переменной
xtime
, который был только что рассмотрен. Аналогично, если параметр
tz
не равен нулю, пользователю возвращается значение часового пояса (time zone), в котором находится операционная система. Этот параметр хранится в переменной
sys_tz
. Если при копировании в пространство пользователя значения абсолютного времени или часового пояса возникли ошибки, то
полностью перекрывает его возможности. Библиотека функций языка С также предоставляет другие функции, связанные с абсолютным временем, такие как
ftime
и
ctime
.
Системный вызов
settimeofday
позволяет установить абсолютное время в указанное значение. Для того чтобы его выполнить, процесс должен иметь возможность использования
CAP_SYS_TIME
.
58
ля некоторых аппаратных платформ функция
sys_time
не реализована, а вместо этого она эмулируется библиотекой функций языка С на основании вызова
gettimeofday
.
Если не считать обновления переменной
xtime
, то ядро не так часто использует абсолютное время, как пространство пользователя. Одно важное исключение— это код файловых систем, который хранят в индексах файлов значения моментов времени доступа к файлам.
Таймеры
Таймеры (timers), или, как их еще иногда называют, динамические таймеры, или таймеры ядра, необходимы для управления ходом времени в ядре. Коду ядра часто необходимо откладывать выполнение некоторых функций на более позднее время. Здесь намеренно выбрано не очень четкое понятие "позже". Назначение механизма нижних половин — это не задерживать выполнение, а не выполнять работу прямо сейчас. В связи с этим необходим инструмент, который позволяет задержать выполнение работы на некоторый интервал времени. Если этот интервал времени не очень маленький, но и не очень большой, то решение проблемы — таймеры ядра.
Таймеры очень легко использовать. Необходимо выполнить некоторые начальные действия, указать момент времени окончания ожидания, указать функцию, которая будет выполнена, когда закончится интервал времени ожидания, и активизировать таймер. Указанная функция будет выполнена, когда закончится интервал времени таймера. Таймеры не являются циклическими. Когда заканчивается интервал времени ожидания, таймер ликвидируется. Это одна из причин, почему таймеры называют динамическими[59] . Таймеры постоянно создаются и ликвидируются, на количество таймеров не существует ограничений. Использование таймеров очень популярно во всех частях ядра.
59
Другая причина состоит в том, что в ядрах старых версий (до 2.3) существовали статические таймеры. Такие таймеры создавались во время компиляции, а не во время выполнения. Они имели ограниченные возможности и из-за их отсутствия сейчас никто не огорчается.
Использование таймеров
Таймеры представлены с помощью структур
timer_list
, которая определена в файле
<linux/timer.h>
следующим образом.
struct timer_list {
struct list_head entry; /* таймеры хранятся в связанном списке */
unsigned long expires; /* время окончание срока ожидания в
импульсах системного таймера (jiffies) */
spinlock_t lock; /* блокировка для защиты данного таймера */