Files
utun2/src/ntp_time_doc.md
T

6.7 KiB
Raw Blame History

NTP Time

1. Назначение

Асинхронный клиент интернет-NTP поверх UDP/IPv4. Не меняет часы ОС: хранит поправку offset_us = local_time - reference_time, а ntp_time_get_us() возвращает local_time - offset_us. Положительная поправка означает, что локальные часы спешат. Поправку также может установить ntp_node_time; RTT и таймеры ETCP используют монотонное время независимо от неё.

2. Как пользоваться

Вызвать ntp_time_init() в uasync-потоке ядра после создания instance->config и instance->ua. Исходное состояние нулевое либо после destroy; активный повторный init возвращает -1, сохраняя прежнее состояние. Обычный init сбрасывает прежнюю поправку и тестовые адреса, копирует список серверов и ставит таймер с задержкой 0. Первый цикл выполняется при обработке uasync, не внутри init.

Цикл начинает с server_current, обходит каждый сервер не более одного раза и заканчивается при первом корректном ответе. Имена разрешаются асинхронно через A-запросы DNS; используется первый полученный IPv4-адрес. После разрешения адреса — до трёх UDP-запросов с ожиданием по 2 с. Каждая отправка имеет новый timestamp; ответ должен содержать timestamp последней отправки в originate. Немедленная ошибка DNS/сокета/отправки/регистрации прекращает попытку этого сервера и переводит к следующему без рекурсии. Ошибки DNS из callback не сбрасывают остаток обхода.

После успеха следующий цикл начинается с сервера, следующего за ответившим; после полного отказа исходный индекс сохраняется. Между завершением цикла и следующим запуском — resync_interval_sec (по умолчанию 3600 с). При активном опросе допустим интервал 1..INT_MAX/10000 секунд: таймеры принимают int в единицах 0,1 мс. Ошибка создания первого таймера возвращается из init; ошибка создания очередного таймера логируется и прекращает автоматические повторы до переинициализации.

Успех устанавливает synced, offset_us, last_sync_tb и вызывает ntp_node_sync_peers(). Полный отказ серверов сохраняет последнюю поправку. reachable отражает успех последнего завершённого интернет-NTP-цикла; изначально он оптимистично равен 1 даже при отключённом опросе. Его изменение обновляет адреса узла через topo_node_update_my_addresses(); это косвенный признак для классификации сокетов, а не гарантия доступа в интернет.

Тестовые подмены устанавливаются после init, до первого poll:

  • ntp_time_set_test_addr(ntp, ip, port) — IPv4-литерал и порт единственного сервера цикла, без DNS и перехода к конфигурационным серверам. port=0 отключает подмену. Конфигурационный список должен оставаться непустым.
  • ntp_time_set_test_dns(ntp, ip, dns_port, ntp_port) — IPv4 DNS-сервер и порт NTP для конфигурационного списка (ntp_port=0 означает 123). test_addr имеет приоритет. dns_port=0 отключает подмену DNS.

Нюансы:

  • ntp_enabled=0 или пустой список отключают только интернет-опрос; коррекция от пиров остаётся возможной.
  • До первой коррекции getters возвращают локальное время; synced означает наличие поправки, а не её свежесть.
  • Нет фильтрации серии измерений или гарантии точного времени; сетевые задержки влияют на оценку.
  • destroy вызывается в uasync-потоке вне callbacks модуля, до освобождения ua. Отменяет DNS, сокет и таймеры, освобождает серверы и отключает опрос, сохраняя synced, offset_us, last_sync_tb, reachable.

3. API

Структуры:

  • struct NTP_TIME — состояние коррекции и интернет-опроса; поля и контракты функций описаны в ntp_time.h.

Функции:

  • ntp_time_init(instance) — 0 при успехе (также при отключённом опросе), -1 при ошибке; новые ресурсы при ошибке освобождаются.
  • ntp_time_destroy(instance) — отменяет опрос, сохраняет поправку; NULL и повторный вызов допустимы.
  • ntp_time_set_test_addr(ntp, ip, port) — задать NTP-сервер напрямую (без DNS) для тестов
  • ntp_time_get_us(instance) — скорректированное время в микросекундах (если synced), иначе gettimeofday()
  • ntp_time_get_seconds(instance) — скорректированное время в секундах (time_t)
  • ntp_time_is_synced(instance) — ненулевое значение при наличии поправки от NTP/пира, для NULL — 0.
  • Getters для NULL возвращают локальное время; секунды вычисляются целочисленным делением микросекунд с приведением к time_t.