Часы в Linux. Часть 1

Часы в Linux. Часть 1

Семён сохраняет полезное_)

В Linux время не представляется как единый непрерывный поток — вместо этого используется концепция clock domains (доменов времени), каждый из которых служит для определённых задач и обладает уникальными свойствами. Разберём ключевые аспекты этой модели.

Основные типы часов (clock domains)

1.CLOCK_REALTIME 

стандартное «настенное» время (wall‑clock), отсчитываемое от эпохи Unix (UTC). Используется для отображения текущего времени и даты. Подвержено корректировкам (например, при синхронизации по NTP).

2.CLOCK_MONOTONIC 

монотонное время, начинающееся с момента загрузки системы. Не идёт назад, не подвержено скачкам. Идеально для измерения интервалов времени.

3.CLOCK_MONOTONIC_RAW 

«сырое» монотонное время без каких‑либо корректировок со стороны ядра. Отражает чистое аппаратное время (hardware clock). Полезен для задач, требующих максимальной точности без влияния программных поправок.

4.CLOCK_BOOTTIME 

аналогично CLOCK_MONOTONIC, но учитывает время, проведённое в режимах приостановки (suspend). Позволяет получить общее время работы системы, включая периоды «сна».

5.CLOCK_TAI 

время по международной шкале атомного времени (TAI). Линейно, без високосных секунд (leap seconds), что делает его удобным для научных и высокоточных вычислений.

Аппаратные источники времени (clocksource)

Ядро Linux выбирает один активный источник времени из доступных аппаратных компонентов. Выбор основан на точности, стабильности и доступности устройства. Основные варианты:

  • TSC (Time Stamp Counter) - счётчик тактов процессора. Очень точный и быстрый, но может быть ненадёжным на системах с изменяемой частотой процессора.
  • HPET (High Precision Event Timer) высокоточный таймер событий. Обеспечивает стабильную частоту и хорошую точность.
  • ACPI PM Timer-таймер управления питанием ACPI. Менее точный, но широко распространён.
  • ARM arch timer -встроенный таймер архитектуры ARM. Стандартный источник для ARM‑систем.

Проверка текущего источника времени:

cat /sys/devices/system/clocksource/clocksource0/current_clocksource

Как ядро считает время

Ядро преобразует аппаратные такты (cycles) в наносекунды по формуле:

time=base+(cycles×mult≫shift)

где:

  • base — базовое время (начальное смещение);
  • cycles — количество тактов, отсчитываемых аппаратным источником;
  • mult и shift коэффициенты масштабирования, позволяющие точно преобразовать такты в наносекунды без использования операций с плавающей точкой.

vDSO: ускорение чтения времени

vDSO (virtual Dynamic Shared Object) - механизм, позволяющий приложениям получать текущее время без выполнения системного вызова (syscall). Это значительно ускоряет операции чтения времени, особенно для CLOCK_MONOTONIC.

Пример использования:

struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);

Вызов clock_gettime() выполняется в пространстве пользователя, минуя переключение контекста в ядро.

Синхронизация времени

Для поддержания точности системного времени используются два основных протокола:

  1. NTP (Network Time Protocol) - стандартный протокол синхронизации по сети:
  2. step - резкий скачок времени (используется при больших расхождениях);
  3. slew - плавная корректировка скорости хода часов (предпочтительный режим для малых отклонений).
  4. PTP (Precision Time Protocol) - протокол для высокоточной синхронизации (микросекундный и наносекундный диапазон):
  5. hardware timestam ping - аппаратная отметка времени пакетов, минимизирующая задержки в программном стеке;
  6. PHC (PTP Hardware Clock) аппаратные часы, поддерживающие PTP. Доступны через /dev/ptpX.

Практическое использование часов

Для чтения времени из различных доменов используется функция clock_gettime():

struct timespec ts;

// CLOCK_REALTIME
clock_gettime(CLOCK_REALTIME, &ts);

// CLOCK_MONOTONIC
clock_gettime(CLOCK_MONOTONIC, &ts);

// CLOCK_MONOTONIC_RAW
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);

// CLOCK_BOOTTIME
clock_gettime(CLOCK_BOOTTIME, &ts);

// CLOCK_TAI
clock_gettime(CLOCK_TAI, &ts);

Kernel API: внутренние функции ядра

Ядро предоставляет набор функций для работы с различными доменами времени:

  • ktime_get() — возвращает монотонное время (CLOCK_MONOTONIC).
  • ktime_get_raw() — возвращает «сырое» монотонное время (CLOCK_MONOTONIC_RAW).
  • ktime_get_real() — возвращает реальное время (CLOCK_REALTIME).
  • ktime_get_boottime() — возвращает время с учётом приостановки (CLOCK_BOOTTIME).

Внутренняя модель расчёта времени

Ядро поддерживает несколько слоёв времени, вычисляемых на основе базового «сырого» времени (RAW):

RAW =base+delta

MONOTONIC =RAW+freq_adjust

REALTIME =MONOTONIC+offs_real

BOOTTIME =MONOTONIC+offs_boot

TAI ​ =REALTIME+offs_tai

где:

  • RAW — базовое аппаратное время;
  • freq_adjust — поправки на дрейф частоты;
  • offs_real — смещение для UTC (включая NTP‑корректировки);
  • offs_boot — время, проведённое в suspend;
  • offs_tai — смещение между UTC и TAI (учитывает leap seconds).

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

Рекомендации по дальнейшему изучению:

  • Документация ядра: Documentation/timers/
  • Исходный код: kernel/time/
  • Статья «Timekeeping in the Linux Kernel» (Thomas Gleixner)



Report Page