文章目录
团队博客: 汽车电子社区
1. 源码结构和文件组织
1.1 目录结构
src/timesync/
├── timesyncd.c # 主守护进程入口 (9.63 KB)
├── timesyncd-manager.c # 核心管理器实现 (45.09 KB)
├── timesyncd-manager.h # 管理器头文件 (3.51 KB)
├── timesyncd-server.c # 服务器管理 (4.81 KB)
├── timesyncd-server.h # 服务器管理头文件 (1.25 KB)
├── timesyncd-bus.c # D-Bus接口实现 (10.84 KB)
├── timesyncd-bus.h # D-Bus接口头文件 (186 B)
├── timesyncd-conf.c # 配置文件解析 (4.08 KB)
├── timesyncd-conf.h # 配置解析头文件 (430 B)
├── timesyncd-ntp-message.h # NTP协议消息定义 (1.15 KB)
├── timesyncd-forward.h # 前向声明 (371 B)
├── timesyncd-gperf.gperf # 配置解析表生成 (1.39 KB)
├── timesyncd.conf.in # 配置文件模板 (1004 B)
├── test-timesync.c # 单元测试 (1.11 KB)
├── wait-sync.c # 同步等待工具 (8.77 KB)
├── meson.build # 构建配置 (2.28 KB)
├── org.freedesktop.timesync1.conf # D-Bus策略配置
├── org.freedesktop.timesync1.policy # Polkit策略
├── org.freedesktop.timesync1.service # D-Bus服务配置
└── 80-systemd-timesync.list # 系统服务列表
1.2 核心架构概览
┌─────────────────────────────────────────────────────────┐
│ systemd-timesyncd │
├─────────────────────────────────────────────────────────┤
│ timesyncd.c (主进程) │
│ ├─ 时间戳管理 │
│ ├─ 权限管理 │
│ └─ 事件循环启动 │
├─────────────────────────────────────────────────────────┤
│ timesyncd-manager.c (核心管理器) │
│ ├─ NTP协议处理 │
│ ├─ 时间同步算法 │
│ ├─ 服务器管理 │
│ ├─ 网络状态监控 │
│ └─ 时钟调整 │
├─────────────────────────────────────────────────────────┤
│ timesyncd-server.c (服务器管理) │
│ ├─ 服务器类型分类 │
│ ├─ 地址解析 │
│ └─ 连接管理 │
├─────────────────────────────────────────────────────────┤
│ timesyncd-bus.c (D-Bus接口) │
│ ├─ 属性导出 │
│ ├─ 方法调用 │
│ └─ 信号发送 │
└─────────────────────────────────────────────────────────┘
2. 核心功能和架构设计
2.1 Manager 结构体设计
typedef struct Manager {
// 事件循环和总线
sd_bus *bus; // D-Bus连接
sd_event *event; // 事件循环
sd_resolve *resolve; // DNS解析器
// 服务器列表 (4种类型)
LIST_HEAD(ServerName, system_servers); // 系统配置的服务器
LIST_HEAD(ServerName, link_servers); // 网络链接配置的服务器
LIST_HEAD(ServerName, runtime_servers); // 运行时配置的服务器
LIST_HEAD(ServerName, fallback_servers); // 回退服务器
// 网络监控
sd_network_monitor *network_monitor; // 网络状态监控
sd_event_source *network_event_source; // 网络事件源
// 当前连接状态
ServerName *current_server_name; // 当前服务器名称
ServerAddress *current_server_address; // 当前服务器地址
int server_socket; // 服务器套接字
bool talking; // 是否正在通信
bool pending; // 是否有待处理的请求
// NTP协议相关
struct ntp_msg ntpmsg; // NTP消息
struct timespec trans_time; // 传输时间戳
struct timespec origin_time, dest_time; // 起源和目标时间
struct ntp_ts request_nonce; // 请求随机数
// 时间同步算法数据
struct {
double offset; // 偏移量
double delay; // 延迟
} samples[8]; // 历史样本数组
unsigned samples_idx; // 样本索引
double samples_jitter; // 抖动值
// 轮询和重试机制
sd_event_source *event_timer; // 轮询定时器
usec_t poll_interval_usec; // 轮询间隔
usec_t poll_interval_min_usec; // 最小轮询间隔
usec_t poll_interval_max_usec; // 最大轮询间隔
usec_t retry_interval; // 重试间隔
int missed_replies; // 错过的回复数
// 时钟状态
int64_t drift_freq; // 时钟漂移频率
bool synchronized; // 是否已同步
bool jumped; // 是否发生了时间跳跃
bool rtc_local_time; // RTC是否为本地时间
} Manager;
2.2 服务器类型分类
typedef enum ServerType {
SERVER_SYSTEM, // 系统配置的服务器 (timesyncd.conf)
SERVER_FALLBACK, // 回退服务器 (编译时默认)
SERVER_LINK, // 网络链接配置的服务器
SERVER_RUNTIME, // 运行时设置的服务器 (D-Bus API)
_SERVER_TYPE_MAX,
} ServerType;
服务器优先级选择逻辑:
1. 运行时服务器 (最高优先级)
2. 系统配置服务器
3. 网络链接服务器
4. 回退服务器 (最低优先级)
3. NTP协议实现
3.1 NTP消息结构
struct ntp_msg {
uint8_t field; // LI(2) + VN(3) + Mode(3)
uint8_t stratum; // 层级
int8_t poll; // 轮询间隔
int8_t precision; // 精度
struct ntp_ts_short root_delay; // 根延迟
struct ntp_ts_short root_dispersion; // 根离散
char refid[4]; // 参考标识
struct ntp_ts reference_time; // 参考时间
struct ntp_ts origin_time; // 起源时间 (T1)
struct ntp_ts recv_time; // 接收时间 (T2)
struct ntp_ts trans_time; // 传输时间 (T3)
} _packed_;
3.2 NTP时间戳计算
核心算法实现 (RFC 5905):
/* 时间戳计算公式:
* delay = (T4 - T1) - (T3 - T2)
* offset = ((T2 - T1) + (T3 - T4)) / 2
*/
origin = ts_to_d(&m->trans_time) + OFFSET_1900_1970; // T1
receive = ntp_ts_to_d(&ntpmsg.recv_time); // T2
trans = ntp_ts_to_d(&ntpmsg.trans_time); // T3
dest = ts_to_d(recv_time) + OFFSET_1900_1970; // T4
offset = ((receive - origin) + (trans - dest)) / 2;
delay = (dest - origin) - (trans - receive);
root_distance = ntp_ts_short_to_d(&ntpmsg.root_delay) / 2 +
ntp_ts_short_to_d(&ntpmsg.root_dispersion);
3.3 NTP请求发送流程
static int manager_send_request(Manager *m) {
struct ntp_msg ntpmsg = {
.field = NTP_FIELD(0, 4, NTP_MODE_CLIENT), // LI=0, VN=4, Mode=Client
};
// 生成64位随机数作为传输时间戳
random_bytes(&m->request_nonce, sizeof(m->request_nonce));
ntpmsg.trans_time = m->request_nonce;
// 记录发送时间 (尽可能接近实际发送时间)
assert_se(clock_gettime(CLOCK_BOOTTIME, &m->trans_time_mon) >= 0);
assert_se(clock_gettime(CLOCK_REALTIME, &m->trans_time) >= 0);
// 发送UDP数据包
len = sendto(m->server_socket, &ntpmsg, sizeof(ntpmsg), MSG_DONTWAIT,
&m->current_server_address->sockaddr.sa,
m->current_server_address->socklen);
}
4. 时间同步算法
4.1 样本管理和抖动检测
static bool manager_sample_spike_detection(Manager *m, double offset, double delay) {
// 保存当前样本
idx_cur = m->samples_idx;
idx_new = (idx_cur + 1) % ELEMENTSOF(m->samples);
m->samples_idx = idx_new;
m->samples[idx_new].offset = offset;
m->samples[idx_new].delay = delay;
// 计算抖动 (基于最小延迟样本的RMS)
for (idx_min = idx_cur, i = 0; i < ELEMENTSOF(m->samples); i++)
if (m->samples[i].delay > 0 && m->samples[i].delay < m->samples[idx_min].delay)
idx_min = i;
j = 0;
FOREACH_ELEMENT(sample, m->samples)
j += pow(sample->offset - m->samples[idx_min].offset, 2);
m->samples_jitter = sqrt(j / (ELEMENTSOF(m->samples) - 1));
// 尖峰检测:偏差 > 3倍抖动视为异常
return fabs(offset - m->samples[idx_cur].offset) > 3 * jitter;
}
4.2 动态轮询间隔调整
static void manager_adjust_poll(Manager *m, double offset, bool spike) {
if (m->poll_resync) {
// 重新同步时使用最小间隔
m->poll_interval_usec = m->poll_interval_min_usec;
return;
}
// 偏移量过大时使用最小轮询间隔
if (!spike && fabs(offset) > NTP_ACCURACY_SEC) {
m->poll_interval_usec = m->poll_interval_min_usec;
return;
}
// 偏移量很小时增加轮询间隔
if (fabs(offset) < NTP_ACCURACY_SEC * 0.25) {
if (m->poll_interval_usec < m->poll_interval_max_usec)
m->poll_interval_usec *= 2;
return;
}
// 偏移量较大或检测到尖峰时减少轮询间隔
if (spike || fabs(offset) > NTP_ACCURACY_SEC * 0.75) {
if (m->poll_interval_usec > m->poll_interval_min_usec)
m->poll_interval_usec /= 2;
}
}
轮询间隔范围:
- 最小间隔:32秒 (符合NTP协议要求的最小15秒)
- 最大间隔:2048秒
- 精度目标:0.2秒
4.3 时钟调整策略
static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
struct timex tmx;
if (fabs(offset) < NTP_MAX_ADJUST) {
// 小偏移量:渐进调整 (slew)
tmx = (struct timex) {
.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST,
.status = STA_PLL,
.offset = offset * NSEC_PER_SEC, // 转换为纳秒
.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4,
};
} else {
// 大偏移量:直接设置 (jump)
tmx = (struct timex) {
.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET,
.time.tv_sec = (long)offset,
.time.tv_usec = (offset - (double)(long)offset) * NSEC_PER_SEC,
};
m->jumped = true;
}
// 处理闰秒
switch (leap_sec) {
case 1: tmx.status |= STA_INS; break; // 插入闰秒
case -1: tmx.status |= STA_DEL; break; // 删除闰秒
}
return clock_adjtime(CLOCK_REALTIME, &tmx);
}
调整阈值:
- 渐进调整:< 0.4秒 (低于内核0.5秒限制)
- 直接跳跃:≥ 0.4秒
5. 服务器管理
5.1 服务器连接管理流程
int manager_connect(Manager *m) {
// 1. 断开当前连接
manager_disconnect(m);
// 2. 选择下一个服务器地址
if (m->current_server_address && m->current_server_address->addresses_next)
manager_set_server_address(m, m->current_server_address->addresses_next);
else {
// 3. 选择下一个服务器
if (m->current_server_name && m->current_server_name->names_next)
manager_set_server_name(m, m->current_server_name->names_next);
else {
// 4. 重新开始服务器轮询
ServerName *f;
if (!m->current_server_name || m->current_server_name->type == SERVER_LINK) {
f = m->runtime_servers; // 优先级1:运行时
if (!f) f = m->system_servers; // 优先级2:系统
if (!f) f = m->link_servers; // 优先级3:网络
} else {
f = m->link_servers; // 优先级3:网络
if (f) restart = false;
else {
f = m->runtime_servers; // 优先级1:运行时
if (!f) f = m->system_servers; // 优先级2:系统
}
}
if (!f) f = m->fallback_servers; // 优先级4:回退
}
}
// 5. DNS解析
struct addrinfo hints = {
.ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG,
.ai_socktype = SOCK_DGRAM,
.ai_family = socket_ipv6_is_supported() ? AF_UNSPEC : AF_INET,
};
r = resolve_getaddrinfo(m->resolve, &m->resolve_query,
m->current_server_name->string, "123", &hints,
manager_resolve_handler, NULL, m);
}
5.2 DNS解析处理
static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, Manager *m) {
if (ret != 0) {
log_debug("Failed to resolve %s: %s", m->current_server_name->string, gai_strerror(ret));
return manager_connect(m); // 尝试下一个服务器
}
// 处理所有返回的地址
for (; ai; ai = ai->ai_next) {
if (!IN_SET(ai->ai_addr->sa_family, AF_INET, AF_INET6))
continue; // 只支持IPv4/IPv6
r = server_address_new(m->current_server_name, &a,
(const union sockaddr_union*) ai->ai_addr, ai->ai_addrlen);
}
if (!m->current_server_name->addresses) {
log_error("Failed to find suitable address for host %s.", m->current_server_name->string);
return manager_connect(m);
}
// 使用第一个地址开始连接
manager_set_server_address(m, m->current_server_name->addresses);
return manager_begin(m);
}
5.3 服务器重试机制
重试策略:
- 最大错过的回复数:2次 (NTP_MAX_MISSED_REPLIES)
- 重试间隔:从15秒开始,每次增加1/3,最大6分钟
- 连接重试:默认30秒间隔
- 服务器轮询:当所有服务器都尝试过后,根据轮询间隔等待
6. D-Bus接口实现
6.1 D-Bus服务定义
Service: org.freedesktop.timesync1
Object Path: /org/freedesktop/timesync1
Interface: org.freedesktop.timesync1.Manager
6.2 主要属性
// 服务器列表属性
SD_BUS_PROPERTY("LinkNTPServers", "as", property_get_servers,
offsetof(Manager, link_servers), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
SD_BUS_PROPERTY("SystemNTPServers", "as", property_get_servers,
offsetof(Manager, system_servers), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
SD_BUS_PROPERTY("RuntimeNTPServers", "as", property_get_servers,
offsetof(Manager, runtime_servers), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
SD_BUS_PROPERTY("FallbackNTPServers", "as", property_get_servers,
offsetof(Manager, fallback_servers), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
// 当前连接状态
SD_BUS_PROPERTY("ServerName", "s", property_get_current_server_name,
offsetof(Manager, current_server_name), 0)
SD_BUS_PROPERTY("ServerAddress", "(iay)", property_get_current_server_address,
offsetof(Manager, current_server_address), 0)
// 同步状态和配置
SD_BUS_PROPERTY("PollIntervalUSec", "t", bus_property_get_usec,
offsetof(Manager, poll_interval_usec), 0)
SD_BUS_PROPERTY("NTPMessage", "(uuuuittayttttbtt)", property_get_ntp_message, 0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
SD_BUS_PROPERTY("Frequency", "x", NULL, offsetof(Manager, drift_freq), 0)
6.3 主要方法
// 设置运行时NTP服务器
SD_BUS_METHOD_WITH_ARGS("SetRuntimeNTPServers",
SD_BUS_ARGS("as", runtime_servers),
SD_BUS_NO_RESULT,
method_set_runtime_servers,
SD_BUS_VTABLE_UNPRIVILEGED)
方法实现:
static int method_set_runtime_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_strv_free_ char **msg_names = NULL;
Manager *m = ASSERT_PTR(userdata);
// 验证权限
r = bus_verify_polkit_async(message, "org.freedesktop.timesync1.set-runtime-servers",
NULL, &m->polkit_registry, error);
if (r == 0) return 1; // Polkit异步回调
// 清除现有运行时服务器
manager_flush_runtime_servers(m);
// 添加新服务器
STRV_FOREACH(name, msg_names) {
r = server_name_new(m, NULL, SERVER_RUNTIME, *name);
if (r < 0) {
manager_flush_runtime_servers(m);
return log_error_errno(r, "Failed to add runtime server '%s': %m", *name);
}
}
// 立即尝试连接新服务器
m->exhausted_servers = true;
manager_set_server_name(m, NULL);
(void) manager_connect(m);
return sd_bus_reply_method_return(message, NULL);
}
7. 时间源选择算法
7.1 服务器选择优先级
优先级1: Runtime Servers (运行时配置)
↓ 通过 D-Bus API 动态设置
↓ 立即生效,覆盖其他配置
优先级2: System Servers (系统配置)
↓ /etc/systemd/timesyncd.conf
↓ 管理员手动配置
优先级3: Link Servers (网络配置)
↓ systemd-networkd 配置
↓ 每个网络接口独立配置
优先级4: Fallback Servers (回退配置)
↓ 编译时默认配置
↓ 确保基本可用性
7.2 服务器质量评估
// 服务器质量检查
if (NTP_FIELD_LEAP(ntpmsg.field) == NTP_LEAP_NOTINSYNC ||
ntpmsg.stratum == 0 || ntpmsg.stratum >= 16) {
log_debug("Server is not synchronized. Disconnecting.");
return manager_connect(m);
}
// 检查版本支持
if (!IN_SET(NTP_FIELD_VERSION(ntpmsg.field), 3, 4)) {
log_debug("Response NTPv%d. Disconnecting.", NTP_FIELD_VERSION(ntpmsg.field));
return manager_connect(m);
}
// 检查根距离
root_distance = ntp_ts_short_to_d(&ntpmsg.root_delay) / 2 +
ntp_ts_short_to_d(&ntpmsg.root_dispersion);
if (root_distance > (double) m->root_distance_max_usec / (double) USEC_PER_SEC) {
log_info("Server has too large root distance. Disconnecting.");
return manager_connect(m);
}
质量标准:
- 层级:1-15 (0和16+无效)
- 版本:NTPv3或NTPv4
- 根距离:< 5秒 (可配置)
- 同步状态:必须已同步
8. 同步状态管理
8.1 同步状态标志
// 状态指示文件
#define SYNCHRONIZED_FILE "/run/systemd/timesync/synchronized"
// 状态变量
bool synchronized; // 是否曾经成功同步
bool talking; // 是否正在与服务器通信
bool pending; // 是否有待处理的请求
bool jumped; // 是否发生了时间跳跃
bool spike; // 当前样本是否为异常值
8.2 状态转换流程
启动状态
↓
选择服务器 → DNS解析 → 建立连接
↓
发送NTP请求
↓
接收响应
↓
质量检查 → 异常检测
↓ ↓
通过 失败
↓ ↓
时钟调整 尝试下一服务器
↓
设置同步标志
↓
调整轮询间隔
↓
定时重复同步
8.3 初始同步处理
if (!spike && !m->synchronized) {
m->synchronized = true;
log_struct(LOG_INFO,
LOG_MESSAGE("Initial clock synchronization to %s.",
FORMAT_TIMESTAMP_STYLE(dts.realtime, TIMESTAMP_US)),
LOG_MESSAGE_ID(SD_MESSAGE_TIME_SYNC_STR),
LOG_ITEM("MONOTONIC_USEC=" USEC_FMT, dts.monotonic),
LOG_ITEM("REALTIME_USEC=" USEC_FMT, dts.realtime),
LOG_ITEM("BOOTTIME_USEC=" USEC_FMT, dts.boottime));
// 创建同步状态文件
r = touch("/run/systemd/timesync/synchronized");
}
9. 与其他模块的集成
9.1 网络管理集成
// 监听网络状态变化
static int manager_network_monitor_listen(Manager *m) {
r = sd_network_monitor_new(&m->network_monitor, NULL);
if (r == -ENOENT) {
log_info("systemd does not appear to be running, not listening for systemd-networkd events.");
return 0;
}
fd = sd_network_monitor_get_fd(m->network_monitor);
events = sd_network_monitor_get_events(m->network_monitor);
return sd_event_add_io(m->event, &m->network_event_source, fd, events,
manager_network_event_handler, m);
}
// 网络事件处理
static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
bool changed = manager_network_read_link_servers(m);
bool online = network_is_online();
bool connected = manager_is_connected(m);
if (connected && !online) {
log_info("No network connectivity, watching for changes.");
manager_disconnect(m);
} else if ((!connected || changed) && online) {
log_info("Network configuration changed, trying to establish connection.");
// 重新建立连接
}
}
9.2 systemd-networkd集成
// 从网络配置读取NTP服务器
static bool manager_network_read_link_servers(Manager *m) {
_cleanup_strv_free_ char **ntp = NULL;
r = sd_network_get_ntp(&ntp); // 获取网络配置的NTP服务器
if (r < 0) goto clear;
// 标记现有服务器
LIST_FOREACH(names, n, m->link_servers)
n->marked = true;
// 添加新服务器,取消未匹配服务器的标记
STRV_FOREACH(i, ntp) {
bool found = false;
LIST_FOREACH(names, n, m->link_servers)
if (streq(n->string, *i)) {
n->marked = false;
found = true;
break;
}
if (!found) {
server_name_new(m, NULL, SERVER_LINK, *i);
changed = true;
}
}
// 删除标记的服务器
LIST_FOREACH(names, n, m->link_servers)
if (n->marked) {
server_name_free(n);
changed = true;
}
}
9.3 内核时间管理集成
// 使用adjtimex系统调用调整时钟
static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
struct timex tmx = {
.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR,
.status = STA_PLL, // 启用相位锁定环
.offset = offset * NSEC_PER_SEC,
.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4,
};
// 处理RTC本地时间
if (m->rtc_local_time)
tmx.status |= STA_UNSYNC; // 禁用内核11分钟模式
// 调用内核接口
if (clock_adjtime(CLOCK_REALTIME, &tmx) < 0)
return -errno;
// 保存频率漂移
m->drift_freq = tmx.freq;
return 0;
}
9.4 时间持久化
// 保存时间到文件系统
static int manager_save_time_and_rearm(Manager *m, usec_t t) {
// 更新时间戳文件,用于系统重启后恢复
r = touch_file(TIMESYNCD_CLOCK_FILE, false, t, UID_INVALID, GID_INVALID, MODE_INVALID);
if (r < 0)
log_debug_errno(r, "Failed to update "TIMESYNCD_CLOCK_FILE", ignoring: %m");
return manager_setup_save_time_event(m);
}
// 启动时加载保存的时间
static int load_clock_timestamp(uid_t uid, gid_t gid) {
usec_t epoch = TIME_EPOCH * USEC_PER_SEC, ct;
// 打开时间戳文件
fd = open(TIMESYNCD_CLOCK_FILE, O_RDWR | O_CLOEXEC, 0644);
if (fd < 0) {
// 文件不存在,使用编译时的epoch时间
r = touch_file(TIMESYNCD_CLOCK_FILE, false, epoch, uid, gid, 0644);
} else {
// 使用文件时间戳中的较新时间
epoch = MAX(epoch, timespec_load(&st.st_mtim));
}
// 如果当前时间早于保存的时间,前进时钟
ct = now(CLOCK_REALTIME);
if (ct <= epoch) {
if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch + 1)) < 0)
log_warning_errno(errno, "Failed to advance system clock, ignoring: %m");
}
}
10. 配置系统
10.1 配置文件格式
# /etc/systemd/timesyncd.conf
[Time]
NTP=time1.example.com time2.example.com
FallbackNTP=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org
RootDistanceMaxSec=5
PollIntervalMinSec=32
PollIntervalMaxSec=2048
ConnectionRetrySec=30
SaveIntervalSec=60
10.2 配置解析实现
// 使用gperf生成的配置解析表
static const char * const server_type_table[_SERVER_TYPE_MAX] = {
[SERVER_SYSTEM] = "system",
[SERVER_FALLBACK] = "fallback",
[SERVER_LINK] = "link",
[SERVER_RUNTIME] = "runtime",
};
// timesyncd-gperf.gperf定义
Time.NTP, config_parse_servers, SERVER_SYSTEM, 0
Time.FallbackNTP, config_parse_servers, SERVER_FALLBACK, 0
Time.RootDistanceMaxSec, config_parse_sec, 0, offsetof(Manager, root_distance_max_usec)
Time.PollIntervalMinSec, config_parse_sec, 0, offsetof(Manager, poll_interval_min_usec)
Time.PollIntervalMaxSec, config_parse_sec, 0, offsetof(Manager, poll_interval_max_usec)
11. 数据流程图
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 启动时间同步 │───▶│ 服务器选择 │───▶│ DNS解析 │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 建立UDP连接 │◀───│ 选择服务器地址 │◀───│ 地址解析完成 │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ 发送NTP请求 │───▶│ 等待响应 │
└─────────────────┘ └──────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ 接收NTP响应 │◀───│ 超时重试 │
└─────────────────┘ └──────────────────┘
│
▼
┌─────────────────┐
│ 消息验证 │
└─────────────────┘
│
▼
┌─────────────────┐
│ 时间计算 │
└─────────────────┘
│
▼
┌─────────────────┐
│ 异常检测 │
└─────────────────┘
│
▼
┌─────────────────┐
│ 时钟调整 │
└─────────────────┘
│
▼
┌─────────────────┐
│ 状态更新 │
└─────────────────┘
│
▼
┌─────────────────┐
│ 调整轮询间隔 │
└─────────────────┘
12. 软件架构图
┌─────────────────────────────────────────────────────────────────────────────┐
│ systemd-timesyncd 架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ D-Bus API │ │ 配置管理 │ │ 网络监控 │ │
│ │ │ │ │ │ │ │
│ │ • 属性查询 │ │ • 配置文件解析 │ │ • 网络状态变化 │ │
│ │ • 方法调用 │ │ • 服务器列表管理 │ │ • DNS解析 │ │
│ │ • 信号发送 │ │ • 参数验证 │ │ • 链路配置读取 │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────────┘ │
│ │ │ │ │
│ └───────────────────────┼───────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────┼─────────────────────────────────────┐ │
│ │ 核心管理器 (Manager) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 服务器管理 │ │ NTP协议处理 │ │ 时间同步算法 │ │ 状态管理 │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • 服务器选择 │ │ • 消息构建 │ │ • 样本收集 │ │ • 同步标志 │ │ │
│ │ │ • 地址解析 │ │ • 响应解析 │ │ • 抖动计算 │ │ • 状态持久化 │ │ │
│ │ │ • 连接管理 │ │ • 验证检查 │ │ • 异常检测 │ │ • 事件通知 │ │ │
│ │ │ • 故障转移 │ │ • 时间戳计算 │ │ • 间隔调整 │ │ • 监控报告 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────┼─────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────┼─────────────────────────────────────┐ │
│ │ 系统接口层 │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 网络通信 │ │ 时钟控制 │ │ 存储管理 │ │ 事件循环 │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • UDP套接字 │ │ • adjtimex │ │ • 时间戳文件 │ │ • sd-event │ │ │
│ │ │ • IPv4/IPv6 │ │ • 时钟设置 │ │ • 状态文件 │ │ • 定时器 │ │ │
│ │ │ • 超时处理 │ │ • 频率调整 │ │ • 配置文件 │ │ • 信号处理 │ │ │
│ │ │ • 重试机制 │ │ • 闰秒处理 │ │ • 权限管理 │ │ • I/O多路复用│ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────┼─────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────┼─────────────────────────────────────┐ │
│ │ 外部系统集成 │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 内核时钟 │ │ systemd- │ │ systemd- │ │ 外部NTP服务 │ │ │
│ │ │ 子系统 │ │ networkd │ │ resolved │ │ 器 │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • PLL控制 │ │ • 网络配置 │ │ • DNS解析 │ │ • 时间源 │ │ │
│ │ │ • 11分钟模式 │ │ • 接口状态 │ │ • 缓存管理 │ │ • 层级结构 │ │ │
│ │ │ • 漂移补偿 │ │ • 链路监控 │ │ • 域名解析 │ │ • 根距离 │ │ │
│ │ │ • 状态报告 │ │ • 事件通知 │ │ • 地址验证 │ │ • 精度保证 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
13. 总结
systemd-timesyncd 是一个设计精良的轻量级NTP客户端,具有以下特点:
设计优势:
1. 模块化架构:清晰的职责分离,便于维护和扩展
2. 多层次服务器配置:支持运行时、系统、网络、回退四种配置
3. 智能时间同步算法:结合抖动检测、异常过滤、动态轮询
4. 完善的集成机制:与systemd生态深度融合
5. 轻量级实现:资源占用少,适合嵌入式和容器环境
核心特性:
- RFC 5905 NTP协议兼容
- 渐进式和跳跃式时钟调整
- 网络状态自适应
- 时间戳持久化
- D-Bus API接口
- 完善的错误处理和重试机制
适用场景:
- 桌面和服务器系统的基础时间同步
- 容器和虚拟机环境
- 嵌入式设备和IoT设备
- 对精度要求不是特别高的应用场景
这个模块充分体现了systemd的设计理念:简洁、高效、可靠、易集成。

315

被折叠的 条评论
为什么被折叠?



