adjtimex(2) тонкая настройка часов в ядре

ОБЗОР

#define _BSD_SOURCE /* Смотрите feature_test_macros(7) */
#include <sys/timex.h>
int adjtimex(struct timex *buf);

ОПИСАНИЕ

В Linux для подстройки часов используется алгоритм Дэвида Л. Миллса (David L. Mills) (см. RFC 5905). Системный вызов adjtimex() читает и (необязательно) устанавливает параметры подстройки для этого алгоритма. В качестве аргумента используется указатель на структуру timex, по значениям в полях которой осуществляется обновление параметров ядра и в этой же структуре происходит возврат текущих значений параметров ядра. Данная структура объявлена так:

struct timex {
    int  modes;      /* выбор режима */
    long offset;     /* смещение по времени; в наносекундах, если
                        флаг установлен состояния STA_NANO, иначе
                        в микросекундах */
    long freq;       /* частота смещения, в единицах 2^-16 ppm
                        (частей на миллион, смотрите ЗАМЕЧАНИЯ далее) */
    long maxerror;   /* максимальная ошибка (микросекунды) */
    long esterror;   /* ожидаемая ошибка (микросекунды) */
    int  status;     /* команда/состояние для часов */
    long constant;   /* временная константа PLL (phase-locked loop) */
    long precision;  /* точность часов (микросекунды, только чтение) */
    long tolerance;  /* допуск тактовой частоты (ppm, только чтение) */
    struct timeval time;
                     /* текущее время (только чтение, кроме
                        ADJ_SETOFFSET); при возврате в time.tv_usec
                        содержатся наносекунды, если установлен флаг
                        состояния STA_NANO, иначе микросекунды */
    long tick;       /* микросекунд между тиками часов */
    long ppsfreq;    /* частота PPS (импульсов в секунду) (в единицах
                        2^-16 ppm, смотрите ЗАМЕЧАНИЯ далее, только чтение) */
    long jitter;     /* искажение PPS (только чтение); в наносекундах, если
                        установлен флаг состояния STA_NANO, иначе
                        микросекунды */
    int  shift;      /* длительность интервала PPS (секунды, только чтение) */
    long stabil;     /* устойчивость PPS (2^-16 ppm; смотрите ЗАМЕЧАНИЯ,
                        только чтение) */
    long jitcnt;     /* превышение ограничения искажения PPS
                        (только чтение) */
    long calcnt;     /* интервалы калибровки PPS (только чтение) */
    long errcnt;     /* ошибки калибровки PPS (только чтение) */
    long stbcnt;     /* превышение ограничения устойчивости PPS
                        (только чтение) */
    int tai;         /* смещение TAI, задаваемое предыдущей операцией ADJ_TAI
                        (секунды, только чтение, начиная с Linux 2.6.26) */
    /* далее идут байты заполнения, для расширения структуры в будущем */
};

Поле modes определяет какие параметры, если это необходимо, устанавливаются. Является битовой маской, содержащей комбинации (or) нуля или более следующих бит:

ADJ_OFFSET
Установить смещение по времени из buf.offset.
ADJ_FREQUENCY
Установить смещение частоты из buf.freq.
ADJ_MAXERROR
Установить максимальную ошибку времени из buf.maxerror.
ADJ_ESTERROR
Установить ожидаемую ошибку времени из buf.esterror.
ADJ_STATUS
Установить состояние часов из buf.status.
ADJ_TIMECONST
Установить константу времени PLL из buf.constant. Если не задан флаг состояния STA_NANO (смотрите ниже), то ядро добавляет к этому значению 4.
ADJ_SETOFFSET (начиная с Linux 2.6.29)
Добавить buf.time к текущему времени. Если в buf.status указан флаг ADJ_NANO, то значение buf.time.tv_usec считается заданным в наносекундах; в противном случае это микросекунды.
ADJ_MICRO (начиная с Linux 2.6.36)
Выбрать микросекундной разрешение.
ADJ_NANO (начиная с Linux 2.6.36)
Выбрать наносекундной разрешение. Должно быть указано что-то одно: ADJ_MICRO или ADJ_NANO.
ADJ_TAI (начиная с Linux 2.6.26)
Установить смещение TAI (атомное международное время) из buf->константы.

ADJ_TAI не должно использоваться вместе с ADJ_TIMECONST, так как последний режим также использует поле buf->константа.

Подробное описание TAI и различия между TAI и UTC смотрите в BIPM

ADJ_TICK
Установить значение тика из buf.tick.

Также в modes можно указывать любое из следующих значений (многобитовая маска), биты которых нельзя указать в modes:

ADJ_OFFSET_SINGLESHOT
Старый вариант adjtime(): (постепенно) корректировать время значением, указанным в buf.offset, которое задаётся в микросекундах.
ADJ_OFFSET_SS_READ (работает, начиная с Linux 2.6.28)
Получить (в buf.offset) остаток необходимого откорректированного времени после выполнения операции ADJ_OFFSET_SINGLESHOT. Это возможность была добавлена в Linux 2.6.24, но работала неправильно до Linux 2.6.28.

Обычные пользователи могут задавать в modes значение ноль или ADJ_OFFSET_SS_READ. Только суперпользователь может задавать любые параметры.

Поле buf.status представляет собой битовую маску, используется для установки/получения битов состояния, связанных с реализацией NTP. Одни биты в маске можно и читать, и изменять, другие — только читать.

STA_PLL
Включить обновление фазовой подстройки частоты (PLL) (чтение/запись) через ADJ_OFFSET.
STA_PPSFREQ
Включить частотную дисциплину обслуживания PPS (чтение/запись).
STA_PPSTIME
Включить временную дисциплину обслуживания PPS (чтение/запись).
STA_FLL
Выбрать режим частотной подстройки частоты (FLL) (чтение/запись).
STA_INS
Вставить високосную секунду (чтение/запись).
STA_DEL
Удалить високосную секунду (чтение/запись).
STA_UNSYNC
Часы не синхронизированы (чтение/запись).
STA_FREQHOLD
Зафиксировать частоту (чтение/запись).
STA_PPSSIGNAL
Присутствует сигнал PPS (только чтение).
STA_PPSJITTER
Превышено искажение сигнала PPS (только чтение).
STA_PPSWANDER
Превышено отклонение сигнала PPS (только чтение).
STA_PPSERROR
Ошибка калибровки сигнала PPS (только чтение).
STA_CLOCKERR
Проблема с аппаратурой часов (только чтение).
STA_NANO (начиная с Linux 2.6.26)
Единица данных (0 = микросекунды, 1 = наносекунды; только чтение). Устанавливается с помощью ADJ_NANO, очищается с помощью ADJ_MICRO.
STA_MODE (начиная с Linux 2.6.26)
Режим (0 = фазовая подстройка частоты, 1 = частотная подстройка частоты; только чтение).
STA_CLK (начиная с Linux 2.6.26)
Источник часов (0 = A, 1 = B; только чтение).

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

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении adjtimex() возвращает состояние часов; то есть одно из следующих значений:
TIME_OK
Часы синхронизированы.
TIME_INS
Вставлена високосная секунда.
TIME_DEL
Удалена високосная секунда.
TIME_OOP
Происходит учёт високосной секунды.
TIME_WAIT
Прошла високосная секунда.
TIME_ERROR
Часы не синхронизированы. Символическое имя TIME_BAD — синоним TIME_ERROR, предоставляется для обратной совместимости.

При ошибке adjtimex() возвращается -1 и изменяется errno.

ОШИБКИ

EFAULT
buf не является указателем на доступную для записи область памяти.
EINVAL
Предпринята попытка установить buf.offset в значение, выходящее за диапазон от -131071 до +131071, или установить buf.status в значение отличное от перечисленных выше, или установить buf.tick в значение за пределами диапазона от 900000/HZ до 1100000/HZ, где HZ — частота прерываний системного таймера.
EPERM
Значение buf.modes не равно 0 или ADJ_OFFSET_SS_READ и вызывающий не имеет необходимых прав. В Linux для этого требуется мандат CAP_SYS_TIME.

ЗАМЕЧАНИЯ

В структуре timex, freq, ppsfreq и stabil ppm (частей на миллион) имеет 16-битную дробную часть, что означает, что значение 1 в одном из этих полей в действительности равно 2^-16 ppm, и 2^16=65536 равно 1 ppm. Это действительно как для входных (в случае freq) так и для выходных значений.

СООТВЕТСТВИЕ СТАНДАРТАМ

adjtimex() --- это специфичный для Linux системный вызов и он не должен использоваться в программах, которые должны быть переносимыми. Существует похожий, более переносимый, более гибкий метод настройки системных часов adjtime(3).