adjtimex 函数系统调用及示例

adjtimex 函数详解

  1. 函数介绍

adjtimex 是Linux系统调用,用于查询和调整系统时钟的状态。它是NTP(Network Time Protocol)守护进程和其他时间同步工具的核心接口,提供了精确的时钟调整和状态查询功能。通过 adjtimex,可以实现高精度的时间同步和时钟管理。

  1. 函数原型
1
2
3
#include <sys/timex.h>
int adjtimex(struct timex *buf);

  1. 功能

adjtimex 允许用户查询系统时钟的状态信息,包括时钟偏移、频率调整、最大误差等,同时支持调整时钟参数以实现精确的时间同步。它是Linux时间子系统的重要组成部分,为高精度时间管理提供了底层支持。

  1. 参数
  • *struct timex buf: 指向timex结构体的指针,用于传递时钟参数和接收状态信息
  1. 返回值
  • 成功: 返回时钟状态(TIME_OK, TIME_INS, TIME_DEL, TIME_OOP, TIME_WAIT, TIME_ERROR)

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • settimeofday: 设置系统时间

  • gettimeofday: 获取系统时间

  • clock_adjtime: 更现代的时钟调整接口

  • ntp_adjtime: NTP时间调整函数

  • clock_gettime/clock_settime: 高精度时钟操作

  1. 示例代码

示例1:基础adjtimex使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>

/**
* 显示时钟状态信息
*/
void show_clock_status(const struct timex *tx) {
printf("=== 时钟状态信息 ===\n");

// 时钟偏移
printf("时钟偏移: %ld.%06ld 秒\n",
tx->offset / 1000000, labs(tx->offset) % 1000000);

// 时钟频率
printf("时钟频率: %ld ppm\n", tx->freq / 65536); // 转换为ppm

// 最大误差
printf("最大误差: %ld 毫秒\n", tx->maxerror);

// 估算误差
printf("估算误差: %ld 毫秒\n", tx->esterror);

// 状态标志
printf("状态标志: 0x%04x\n", tx->status);
printf(" ");
if (tx->status & STA_PLL) printf("STA_PLL ");
if (tx->status & STA_PPSFREQ) printf("STA_PPSFREQ ");
if (tx->status & STA_PPSTIME) printf("STA_PPSTIME ");
if (tx->status & STA_FLL) printf("STA_FLL ");
if (tx->status & STA_INS) printf("STA_INS ");
if (tx->status & STA_DEL) printf("STA_DEL ");
if (tx->status & STA_UNSYNC) printf("STA_UNSYNC ");
if (tx->status & STA_FREQHOLD) printf("STA_FREQHOLD ");
if (tx->status & STA_PPSSIGNAL) printf("STA_PPSSIGNAL ");
if (tx->status & STA_PPSJITTER) printf("STA_PPSJITTER ");
if (tx->status & STA_PPSWANDER) printf("STA_PPSWANDER ");
if (tx->status & STA_PPSERROR) printf("STA_PPSERROR ");
if (tx->status & STA_CLOCKERR) printf("STA_CLOCKERR ");
printf("\n");

// 时钟精度
printf("时钟精度: %ld 毫秒\n", tx->precision);

// PLL时间常数
printf("PLL时间常数: %ld\n", tx->constant);

// PPM容忍度
printf("PPM容忍度: %ld\n", tx->tolerance);

// 时钟状态
printf("时钟状态: ");
switch (tx->state) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_INS: printf("TIME_INS (即将插入闰秒)\n"); break;
case TIME_DEL: printf("TIME_DEL (即将删除闰秒)\n"); break;
case TIME_OOP: printf("TIME_OOP (闰秒处理中)\n"); break;
case TIME_WAIT: printf("TIME_WAIT (等待同步)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (时钟错误)\n"); break;
default: printf("未知状态 (%d)\n", tx->state); break;
}

printf("\n");
}

/**
* 演示基础adjtimex使用方法
*/
int demo_adjtimex_basic() {
struct timex tx;
int result;

printf("=== 基础adjtimex使用示例 ===\n");

// 初始化timex结构体
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式,不修改任何参数

// 调用adjtimex查询时钟状态
printf("1. 查询时钟状态:\n");
result = adjtimex(&tx);

if (result == -1) {
printf(" 查询时钟状态失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:需要CAP_SYS_TIME权限\n");
} else if (errno == EINVAL) {
printf(" 原因:参数无效\n");
}
return -1;
}

printf(" 查询成功\n");
show_clock_status(&tx);

// 显示时钟信息
printf("2. 时钟详细信息:\n");
printf(" 系统时钟: %ld.%06ld 秒\n", tx.time.tv_sec, tx.time.tv_usec);

// 显示频率调整信息
printf("3. 频率调整信息:\n");
printf(" 频率偏移: %ld (系统单位)\n", tx.freq);
printf(" 频率偏移: %.3f ppm\n", (double)tx.freq / 65536.0);

// 显示PLL参数
printf("4. PLL参数:\n");
printf(" PLL偏移: %ld\n", tx.offset);
printf(" PLL最大误差: %ld 毫秒\n", tx.maxerror);
printf(" PLL估算误差: %ld 毫秒\n", tx.esterror);
printf(" PLL时间常数: %ld\n", tx.constant);

// 演示权限检查
printf("\n5. 权限检查:\n");
uid_t uid = getuid();
printf(" 当前用户ID: %d\n", uid);
if (uid == 0) {
printf(" ✓ 具有root权限,可以调整时钟参数\n");
} else {
printf(" ✗ 没有root权限,只能查询时钟状态\n");
printf(" 提示:调整时钟参数需要root权限或CAP_SYS_TIME能力\n");
}

// 演示时钟状态解释
printf("\n=== 时钟状态解释 ===\n");
printf("TIME_OK: 时钟同步正常\n");
printf("TIME_INS: 即将插入闰秒\n");
printf("TIME_DEL: 即将删除闰秒\n");
printf("TIME_OOP: 闰秒处理中\n");
printf("TIME_WAIT: 等待同步\n");
printf("TIME_ERROR: 时钟错误\n");

// 演示状态标志解释
printf("\n=== 状态标志解释 ===\n");
printf("STA_PLL: PLL模式启用\n");
printf("STA_PPSFREQ: PPS频率调整\n");
printf("STA_PPSTIME: PPS时间调整\n");
printf("STA_FLL: 频率锁定环模式\n");
printf("STA_INS: 即将插入闰秒\n");
printf("STA_DEL: 即将删除闰秒\n");
printf("STA_UNSYNC: 时钟未同步\n");
printf("STA_FREQHOLD: 频率保持\n");

return 0;
}

int main() {
return demo_adjtimex_basic();
}

示例2:时钟调整和同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* 模拟NTP时间同步
*/
int simulate_ntp_synchronization() {
struct timex tx;
int result;
double network_delay = 0.05; // 50ms网络延迟
double frequency_drift = 50.0; // 50ppm频率漂移

printf("=== NTP时间同步模拟 ===\n");

// 获取当前时钟状态
printf("1. 获取当前时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf(" 获取时钟状态失败: %s\n", strerror(errno));
return -1;
}

printf(" 当前时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 当前最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 当前估算误差: %ld 毫秒\n", tx.esterror);

// 模拟网络时间查询
printf("\n2. 模拟网络时间查询:\n");
struct timeval network_time;
gettimeofday(&network_time, NULL);

// 模拟时钟偏移(100ms)
long offset_us = 100000; // 100ms偏移
printf(" 检测到时钟偏移: %.3f ms\n", offset_us / 1000.0);
printf(" 网络延迟: %.3f ms\n", network_delay * 1000);
printf(" 频率漂移: %.3f ppm\n", frequency_drift);

// 调整时钟参数
printf("\n3. 调整时钟参数:\n");
if (getuid() == 0) {
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_OFFSET | ADJ_FREQUENCY;
tx.offset = offset_us; // 微秒偏移
tx.freq = (long)(frequency_drift * 65536); // 转换为系统单位

printf(" 设置时钟偏移: %ld 微秒\n", tx.offset);
printf(" 设置频率调整: %.3f ppm\n", (double)tx.freq / 65536.0);

result = adjtimex(&tx);
if (result == -1) {
printf(" 调整时钟参数失败: %s\n", strerror(errno));
} else {
printf(" ✓ 时钟参数调整成功\n");
printf(" 新时钟状态: ");
switch (result) {
case TIME_OK: printf("TIME_OK\n"); break;
case TIME_INS: printf("TIME_INS\n"); break;
case TIME_DEL: printf("TIME_DEL\n"); break;
case TIME_OOP: printf("TIME_OOP\n"); break;
case TIME_WAIT: printf("TIME_WAIT\n"); break;
case TIME_ERROR: printf("TIME_ERROR\n"); break;
default: printf("未知状态 %d\n", result); break;
}
}
} else {
printf(" ✗ 没有权限调整时钟参数\n");
printf(" 提示:需要root权限才能调整时钟\n");
}

// 验证调整结果
printf("\n4. 验证调整结果:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
printf(" 调整后时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 调整后最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 调整后估算误差: %ld 毫秒\n", tx.esterror);
}

return 0;
}

/**
* 演示时钟频率调整
*/
int demo_frequency_adjustment() {
struct timex tx;
int result;

printf("=== 时钟频率调整演示 ===\n");

// 显示原始频率
printf("1. 原始时钟频率:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf(" 查询时钟频率失败: %s\n", strerror(errno));
return -1;
}

double original_freq = (double)tx.freq / 65536.0;
printf(" 原始频率: %.6f ppm\n", original_freq);

// 调整频率(需要root权限)
printf("\n2. 调整时钟频率:\n");
if (getuid() == 0) {
// 增加50ppm频率调整
long freq_adjust = 50 * 65536; // 50ppm转换为系统单位

memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_FREQUENCY;
tx.freq = freq_adjust;

printf(" 设置频率调整: %.3f ppm\n", (double)freq_adjust / 65536.0);

result = adjtimex(&tx);
if (result == -1) {
printf(" 频率调整失败: %s\n", strerror(errno));
} else {
printf(" ✓ 频率调整成功\n");
}

// 验证调整结果
printf("\n3. 验证频率调整:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
double new_freq = (double)tx.freq / 65536.0;
printf(" 调整后频率: %.6f ppm\n", new_freq);
printf(" 频率变化: %.6f ppm\n", new_freq - original_freq);
}

// 恢复原始频率
printf("\n4. 恢复原始频率:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_FREQUENCY;
tx.freq = (long)(original_freq * 65536);

result = adjtimex(&tx);
if (result == -1) {
printf(" 恢复原始频率失败: %s\n", strerror(errno));
} else {
printf(" ✓ 原始频率恢复成功\n");
}
} else {
printf(" ✗ 没有权限调整时钟频率\n");
printf(" 提示:需要root权限才能调整时钟频率\n");
}

return 0;
}

int main() {
printf("=== adjtimex时钟调整演示 ===\n");

// 演示NTP同步模拟
if (simulate_ntp_synchronization() != 0) {
return -1;
}

printf("\n" "=" * 50 "\n");

// 演示频率调整
if (demo_frequency_adjustment() != 0) {
return -1;
}

return 0;
}

示例3:高精度时间同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* 高精度时间同步器结构
*/
typedef struct {
double last_offset; // 上次时钟偏移
double last_frequency; // 上次频率调整
time_t last_sync_time; // 上次同步时间
int sync_count; // 同步次数
double avg_offset; // 平均偏移
double jitter; // 抖动
int precision_ppm; // 精度(ppm)
} precision_sync_t;

/**
* 初始化高精度同步器
*/
int init_precision_sync(precision_sync_t *sync) {
memset(sync, 0, sizeof(precision_sync_t));
sync->precision_ppm = 1; // 1ppm精度
sync->last_sync_time = time(NULL);

printf("高精度时间同步器初始化完成\n");
printf(" 目标精度: %d ppm\n", sync->precision_ppm);

return 0;
}

/**
* 获取高精度时间
*/
int get_high_precision_time(struct timespec *ts) {
struct timex tx;
int result;

memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
return -1;
}

ts->tv_sec = tx.time.tv_sec;
ts->tv_nsec = tx.time.tv_usec * 1000; // 转换为纳秒

return 0;
}

/**
* 计算时钟偏移
*/
double calculate_clock_offset() {
struct timespec system_time, precise_time;
double offset;

// 获取系统时间
if (clock_gettime(CLOCK_REALTIME, &system_time) == -1) {
return 0.0;
}

// 获取高精度时间
if (get_high_precision_time(&precise_time) == -1) {
return 0.0;
}

// 计算偏移(纳秒)
offset = (precise_time.tv_sec - system_time.tv_sec) * 1000000000.0 +
(precise_time.tv_nsec - system_time.tv_nsec);

return offset / 1000000.0; // 转换为毫秒
}

/**
* 演示高精度时间同步
*/
int demo_high_precision_sync() {
precision_sync_t sync;
struct timex tx;
int result;
int sync_attempts = 5;

printf("=== 高精度时间同步演示 ===\n");

// 初始化同步器
printf("1. 初始化高精度同步器:\n");
if (init_precision_sync(&sync) != 0) {
return -1;
}

// 显示初始时钟状态
printf("\n2. 初始时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf(" 获取时钟状态失败: %s\n", strerror(errno));
return -1;
}

printf(" 时钟状态: ");
switch (tx.state) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (错误)\n"); break;
default: printf("状态 %d\n", tx.state); break;
}

printf(" 当前频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 估算误差: %ld 毫秒\n", tx.esterror);
printf(" 时钟精度: %ld 毫秒\n", tx.precision);

// 模拟高精度时间同步过程
printf("\n3. 高精度时间同步过程:\n");

for (int i = 0; i < sync_attempts; i++) {
printf(" 第 %d 次同步:\n", i + 1);

// 模拟网络时间查询
double network_offset = (rand() % 1000 - 500) / 1000.0; // -0.5到0.5毫秒
double network_delay = (rand() % 100) / 1000.0; // 0到0.1毫秒

printf(" 网络偏移: %.3f ms\n", network_offset);
printf(" 网络延迟: %.3f ms\n", network_delay);

// 计算真实的时钟偏移
double actual_offset = calculate_clock_offset();
printf(" 实际偏移: %.3f ms\n", actual_offset);

// 计算综合偏移
double total_offset = network_offset + actual_offset;
printf(" 综合偏移: %.3f ms\n", total_offset);

// 更新同步统计
sync.last_offset = total_offset;
sync.avg_offset = (sync.avg_offset * sync.sync_count + total_offset) / (sync.sync_count + 1);
sync.sync_count++;

// 计算抖动
if (sync.sync_count > 1) {
sync.jitter = fabs(total_offset - sync.avg_offset);
}

printf(" 平均偏移: %.3f ms\n", sync.avg_offset);
printf(" 当前抖动: %.3f ms\n", sync.jitter);

// 如果有root权限,进行时钟调整
if (getuid() == 0) {
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_OFFSET | ADJ_STATUS;
tx.offset = (long)(total_offset * 1000); // 转换为微秒
tx.status = STA_PLL; // 启用PLL模式

printf(" 调整时钟偏移: %ld 微秒\n", tx.offset);

result = adjtimex(&tx);
if (result == -1) {
printf(" 时钟调整失败: %s\n", strerror(errno));
} else {
printf(" ✓ 时钟调整成功\n");
}
} else {
printf(" ℹ 没有权限进行时钟调整\n");
}

// 记录同步时间
sync.last_sync_time = time(NULL);

if (i < sync_attempts - 1) {
sleep(2); // 间隔同步
}
}

// 显示最终同步结果
printf("\n4. 最终同步结果:\n");
printf(" 同步次数: %d\n", sync.sync_count);
printf(" 最后偏移: %.3f ms\n", sync.last_offset);
printf(" 平均偏移: %.3f ms\n", sync.avg_offset);
printf(" 最大抖动: %.3f ms\n", sync.jitter);
printf(" 最后同步: %s", ctime(&sync.last_sync_time));

// 计算同步精度
double sync_accuracy = 1000.0 / pow(10, sync.precision_ppm); // 简化的精度计算
printf(" 同步精度: %.6f 秒\n", sync_accuracy);

// 显示高精度同步优势
printf("\n=== 高精度同步优势 ===\n");
printf("1. 精度提升:\n");
printf(" ✓ 纳秒级时间精度\n");
printf(" ✓ 微秒级偏移调整\n");
printf(" ✓ PPM级频率控制\n");

printf("\n2. 稳定性保障:\n");
printf(" ✓ 抖动抑制\n");
printf(" ✓ 误差累积控制\n");
printf(" ✓ 频率漂移补偿\n");

printf("\n3. 实时性:\n");
printf(" ✓ 快速收敛\n");
printf(" ✓ 动态调整\n");
printf(" ✓ 状态监控\n");

return 0;
}

int main() {
return demo_high_precision_sync();
}

示例4:时间同步监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* 时间同步监控数据结构
*/
typedef struct {
time_t timestamp;
long offset_us; // 偏移(微秒)
long frequency_ppm; // 频率(ppm)
long max_error_ms; // 最大误差(毫秒)
long est_error_ms; // 估算误差(毫秒)
int clock_state; // 时钟状态
int status_flags; // 状态标志
double jitter_ms; // 抖动(毫秒)
} sync_monitor_data_t;

/**
* 时间同步监控器
*/
typedef struct {
sync_monitor_data_t history&#91;100]; // 历史数据
int history_count;
int max_history;
time_t start_time;
int monitoring;
} sync_monitor_t;

/**
* 初始化监控器
*/
int init_sync_monitor(sync_monitor_t *monitor) {
memset(monitor, 0, sizeof(sync_monitor_t));
monitor->max_history = 100;
monitor->start_time = time(NULL);
monitor->monitoring = 1;

printf("时间同步监控器初始化完成\n");
printf(" 最大历史记录数: %d\n", monitor->max_history);
printf(" 启动时间: %s", ctime(&monitor->start_time));

return 0;
}

/**
* 收集时钟状态数据
*/
int collect_clock_data(sync_monitor_t *monitor) {
struct timex tx;
int result;

if (monitor->history_count >= monitor->max_history) {
// 循环覆盖旧数据
memmove(&monitor->history&#91;0], &monitor->history&#91;1],
sizeof(sync_monitor_data_t) * (monitor->max_history - 1));
monitor->history_count = monitor->max_history - 1;
}

sync_monitor_data_t *current = &monitor->history&#91;monitor->history_count];

// 获取时钟状态
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf("获取时钟状态失败: %s\n", strerror(errno));
return -1;
}

// 填充监控数据
current->timestamp = time(NULL);
current->offset_us = tx.offset;
current->frequency_ppm = tx.freq / 65536;
current->max_error_ms = tx.maxerror;
current->est_error_ms = tx.esterror;
current->clock_state = tx.state;
current->status_flags = tx.status;
current->jitter_ms = 0.0; // 简化处理

// 计算抖动(与前一个采样点的差异)
if (monitor->history_count > 0) {
sync_monitor_data_t *previous = &monitor->history&#91;monitor->history_count - 1];
current->jitter_ms = fabs((current->offset_us - previous->offset_us) / 1000.0);
}

monitor->history_count++;

return 0;
}

/**
* 显示时钟状态
*/
void show_clock_status(const sync_monitor_data_t *data) {
printf("时间: %s", ctime(&data->timestamp));
printf(" 时钟偏移: %.3f ms\n", data->offset_us / 1000.0);
printf(" 频率调整: %ld ppm\n", data->frequency_ppm);
printf(" 最大误差: %ld ms\n", data->max_error_ms);
printf(" 估算误差: %ld ms\n", data->est_error_ms);
printf(" 时钟状态: %d\n", data->clock_state);
printf(" 抖动: %.3f ms\n", data->jitter_ms);

printf(" 状态标志: 0x%04x ", data->status_flags);
if (data->status_flags & STA_PLL) printf("STA_PLL ");
if (data->status_flags & STA_UNSYNC) printf("STA_UNSYNC ");
if (data->status_flags & STA_FREQHOLD) printf("STA_FREQHOLD ");
printf("\n");
}

/**
* 分析时间同步质量
*/
void analyze_sync_quality(const sync_monitor_t *monitor) {
if (monitor->history_count < 2) {
printf("数据不足,无法分析同步质量\n");
return;
}

printf("=== 时间同步质量分析 ===\n");

// 计算统计信息
double total_offset = 0, max_offset = 0, min_offset = 999999;
double total_jitter = 0, max_jitter = 0;
long total_error = 0, max_error = 0;
int sync_ok_count = 0;

for (int i = 0; i < monitor->history_count; i++) {
const sync_monitor_data_t *data = &monitor->history&#91;i];
double abs_offset = fabs(data->offset_us / 1000.0);

total_offset += abs_offset;
total_jitter += data->jitter_ms;
total_error += data->est_error_ms;

if (abs_offset > max_offset) max_offset = abs_offset;
if (abs_offset < min_offset) min_offset = abs_offset;
if (data->jitter_ms > max_jitter) max_jitter = data->jitter_ms;
if (data->est_error_ms > max_error) max_error = data->est_error_ms;

if (abs_offset < 10.0) sync_ok_count++; // 10ms以内认为同步良好
}

double avg_offset = total_offset / monitor->history_count;
double avg_jitter = total_jitter / monitor->history_count;
double avg_error = (double)total_error / monitor->history_count;
double sync_quality = (double)sync_ok_count / monitor->history_count * 100;

printf("同步质量统计:\n");
printf(" 平均偏移: %.3f ms\n", avg_offset);
printf(" 最大偏移: %.3f ms\n", max_offset);
printf(" 最小偏移: %.3f ms\n", min_offset);
printf(" 平均抖动: %.3f ms\n", avg_jitter);
printf(" 最大抖动: %.3f ms\n", max_jitter);
printf(" 平均误差: %.3f ms\n", avg_error);
printf(" 最大误差: %ld ms\n", max_error);
printf(" 同步质量: %.1f%%\n", sync_quality);

// 质量评估
printf("\n质量评估:\n");
if (avg_offset < 1.0) {
printf(" ✓ 优秀: 平均偏移 < 1ms\n");
} else if (avg_offset < 10.0) {
printf(" ℹ 良好: 平均偏移 < 10ms\n");
} else {
printf(" ⚠ 需要改善: 平均偏移 > 10ms\n");
}

if (sync_quality > 95.0) {
printf(" ✓ 高可靠性: 同步质量 > 95%%\n");
} else if (sync_quality > 80.0) {
printf(" ℹ 中等可靠性: 同步质量 > 80%%\n");
} else {
printf(" ⚠ 低可靠性: 同步质量 < 80%%\n");
}
}

/**
* 演示时间同步监控
*/
int demo_sync_monitoring() {
sync_monitor_t monitor;
const int monitor_duration = 30; // 监控30秒
time_t start_time, current_time;

printf("=== 时间同步监控演示 ===\n");

// 初始化监控器
printf("1. 初始化监控器:\n");
if (init_sync_monitor(&monitor) != 0) {
return -1;
}

// 开始监控
printf("\n2. 开始时间同步监控:\n");
printf(" 监控时长: %d 秒\n", monitor_duration);
printf(" 采样间隔: 2 秒\n");

start_time = time(NULL);

while (difftime(time(NULL), start_time) < monitor_duration) {
// 收集时钟数据
if (collect_clock_data(&monitor) == 0) {
if (monitor.history_count > 0) {
const sync_monitor_data_t *latest =
&monitor.history&#91;monitor.history_count - 1];

printf("\n--- 采样点 %d ---\n", monitor.history_count);
show_clock_status(latest);
}
} else {
printf("收集时钟数据失败\n");
}

sleep(2); // 2秒采样间隔
}

// 显示监控结果
printf("\n3. 监控结果:\n");
printf(" 总采样点数: %d\n", monitor.history_count);
printf(" 监控时长: %.0f 秒\n", difftime(time(NULL), start_time));

if (monitor.history_count > 0) {
printf(" 最新数据:\n");
const sync_monitor_data_t *latest =
&monitor.history&#91;monitor.history_count - 1];
show_clock_status(latest);
}

// 分析同步质量
printf("\n4. 同步质量分析:\n");
analyze_sync_quality(&monitor);

// 显示历史趋势
printf("\n5. 历史趋势 (最后10个采样点):\n");
int start_index = (monitor.history_count > 10) ? monitor.history_count - 10 : 0;

printf("%-20s %-10s %-8s %-8s %-8s\n",
"时间", "偏移(ms)", "频率(ppm)", "误差(ms)", "抖动(ms)");
printf("%-20s %-10s %-8s %-8s %-8s\n",
"----", "--------", "--------", "--------", "--------");

for (int i = start_index; i < monitor.history_count; i++) {
const sync_monitor_data_t *data = &monitor.history&#91;i];
char time_str&#91;20];
strftime(time_str, sizeof(time_str), "%H:%M:%S", localtime(&data->timestamp));

printf("%-20s %-10.3f %-8ld %-8ld %-8.3f\n",
time_str,
data->offset_us / 1000.0,
data->frequency_ppm,
data->est_error_ms,
data->jitter_ms);
}

return 0;
}

int main() {
return demo_sync_monitoring();
}

示例5:NTP客户端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* NTP服务器信息结构
*/
typedef struct {
char hostname&#91;256];
int port;
double stratum; // 层级
double delay; // 延迟
double offset; // 偏移
double dispersion; // 离散度
time_t last_contact; // 最后联系时间
int reachable; // 是否可达
} ntp_server_t;

/**
* NTP客户端结构
*/
typedef struct {
ntp_server_t servers&#91;5];
int server_count;
int current_server;
double sync_threshold; // 同步阈值(毫秒)
double max_adjustment; // 最大调整值(毫秒)
int sync_interval; // 同步间隔(秒)
} ntp_client_t;

/**
* 初始化NTP客户端
*/
int init_ntp_client(ntp_client_t *client) {
memset(client, 0, sizeof(ntp_client_t));

// 初始化测试服务器
const char *test_servers&#91;] = {
"pool.ntp.org",
"time.google.com",
"time.cloudflare.com",
"ntp.aliyun.com",
NULL
};

printf("=== NTP客户端初始化 ===\n");

for (int i = 0; test_servers&#91;i] && i < 5; i++) {
ntp_server_t *server = &client->servers&#91;i];
strncpy(server->hostname, test_servers&#91;i], sizeof(server->hostname) - 1);
server->hostname&#91;sizeof(server->hostname) - 1] = '\0';
server->port = 123; // NTP标准端口
server->stratum = 2 + i; // 模拟不同层级
server->delay = 0.05 + (rand() / (double)RAND_MAX) * 0.1; // 50-150ms延迟
server->offset = (rand() / (double)RAND_MAX) * 2.0 - 1.0; // -1到1秒偏移
server->dispersion = 0.01 + (rand() / (double)RAND_MAX) * 0.05; // 10-60ms离散度
server->last_contact = time(NULL);
server->reachable = 1; // 模拟可达

client->server_count++;
printf(" 添加服务器 %d: %s (层级: %.0f)\n",
i + 1, server->hostname, server->stratum);
}

client->current_server = 0;
client->sync_threshold = 100.0; // 100ms阈值
client->max_adjustment = 500.0; // 500ms最大调整
client->sync_interval = 60; // 60秒同步间隔

printf(" 同步阈值: %.1f ms\n", client->sync_threshold);
printf(" 最大调整: %.1f ms\n", client->max_adjustment);
printf(" 同步间隔: %d 秒\n", client->sync_interval);

return 0;
}

/**
* 选择最佳NTP服务器
*/
int select_best_ntp_server(ntp_client_t *client) {
int best_server = -1;
double best_quality = 999999.0;

printf("选择最佳NTP服务器:\n");

for (int i = 0; i < client->server_count; i++) {
ntp_server_t *server = &client->servers&#91;i];

if (server->reachable) {
// 计算服务器质量(基于层级、延迟和离散度)
double quality = server->stratum + server->delay * 10 + server->dispersion * 5;

printf(" 服务器 %d (%s): 质量评分 %.3f\n",
i + 1, server->hostname, quality);

if (quality < best_quality) {
best_quality = quality;
best_server = i;
}
}
}

if (best_server != -1) {
client->current_server = best_server;
printf(" 选择最佳服务器: %s\n",
client->servers&#91;best_server].hostname);
}

return best_server;
}

/**
* 模拟NTP时间同步
*/
int simulate_ntp_sync(ntp_client_t *client) {
struct timex tx;
int result;
int best_server = select_best_ntp_server(client);

if (best_server == -1) {
printf("没有可用的NTP服务器\n");
return -1;
}

ntp_server_t *server = &client->servers&#91;best_server];

printf("=== NTP时间同步 ===\n");
printf("同步服务器: %s\n", server->hostname);
printf("服务器层级: %.0f\n", server->stratum);
printf("网络延迟: %.3f 秒\n", server->delay);
printf("时钟偏移: %.3f 秒\n", server->offset);
printf("离散度: %.3f 秒\n", server->dispersion);

// 检查是否需要同步
if (fabs(server->offset) * 1000 > client->sync_threshold) {
printf("时钟偏移过大,需要同步\n");

// 如果有root权限,进行时钟调整
if (getuid() == 0) {
printf("具有root权限,进行时钟调整:\n");

memset(&tx, 0, sizeof(tx));

// 根据偏移大小选择调整策略
if (fabs(server->offset) > 1.0) {
// 大偏移:步进调整
printf(" 大偏移调整:\n");
tx.modes = ADJ_SETOFFSET;
tx.time.tv_sec = (long)server->offset;
tx.time.tv_usec = (long)((server->offset - (long)server->offset) * 1000000);
printf(" 设置时间偏移: %ld.%06ld 秒\n",
tx.time.tv_sec, tx.time.tv_usec);
} else {
// 小偏移:渐进调整
printf(" 渐进调整:\n");
tx.modes = ADJ_OFFSET | ADJ_STATUS;
tx.offset = (long)(server->offset * 1000000); // 转换为微秒
tx.status = STA_PLL;
printf(" 调整偏移: %ld 微秒\n", tx.offset);
}

result = adjtimex(&tx);
if (result == -1) {
printf(" 时钟调整失败: %s\n", strerror(errno));
return -1;
}

printf(" ✓ 时钟调整成功\n");

// 显示调整后的状态
printf(" 调整后状态: ");
switch (result) {
case TIME_OK: printf("TIME_OK\n"); break;
case TIME_INS: printf("TIME_INS\n"); break;
case TIME_DEL: printf("TIME_DEL\n"); break;
case TIME_OOP: printf("TIME_OOP\n"); break;
case TIME_WAIT: printf("TIME_WAIT\n"); break;
case TIME_ERROR: printf("TIME_ERROR\n"); break;
default: printf("状态 %d\n", result); break;
}
} else {
printf("没有root权限,无法进行时钟调整\n");
printf("建议使用NTP守护进程进行时间同步\n");
}
} else {
printf("时钟偏移在可接受范围内,无需调整\n");
}

// 更新服务器联系时间
server->last_contact = time(NULL);

return 0;
}

/**
* 演示NTP客户端功能
*/
int demo_ntp_client() {
ntp_client_t client;
struct timex tx;
int result;

printf("=== NTP客户端功能演示 ===\n");

// 初始化客户端
printf("1. 初始化NTP客户端:\n");
if (init_ntp_client(&client) != 0) {
return -1;
}

// 显示当前系统时间
printf("\n2. 当前系统时间:\n");
struct timeval current_time;
gettimeofday(&current_time, NULL);
printf(" 系统时间: %ld.%06ld\n", current_time.tv_sec, current_time.tv_usec);

// 获取当前时钟状态
printf("\n3. 当前时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
printf(" 时钟状态: ");
switch (tx.state) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (错误)\n"); break;
default: printf("状态 %d\n", tx.state); break;
}
printf(" 时钟偏移: %ld.%06ld 秒\n", tx.offset / 1000000, labs(tx.offset) % 1000000);
printf(" 时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 估算误差: %ld 毫秒\n", tx.esterror);
}

// 模拟NTP同步
printf("\n4. 模拟NTP时间同步:\n");
if (simulate_ntp_sync(&client) != 0) {
printf("NTP同步失败\n");
return -1;
}

// 显示同步后状态
printf("\n5. 同步后时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
printf(" 时钟状态: ");
switch (result) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_INS: printf("TIME_INS (即将插入闰秒)\n"); break;
case TIME_DEL: printf("TIME_DEL (即将删除闰秒)\n"); break;
case TIME_OOP: printf("TIME_OOP (闰秒处理中)\n"); break;
case TIME_WAIT: printf("TIME_WAIT (等待同步)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (时钟错误)\n"); break;
default: printf("状态 %d\n", result); break;
}
printf(" 时钟偏移: %ld.%06ld 秒\n", tx.offset / 1000000, labs(tx.offset) % 1000000);
printf(" 时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 估算误差: %ld 毫秒\n", tx.esterror);
}

// 显示NTP服务器信息
printf("\n6. NTP服务器信息:\n");
for (int i = 0; i < client.server_count; i++) {
ntp_server_t *server = &client.servers&#91;i];
printf(" 服务器 %d: %s\n", i + 1, server->hostname);
printf(" 层级: %.0f\n", server->stratum);
printf(" 延迟: %.3f 秒\n", server->delay);
printf(" 偏移: %.3f 秒\n", server->offset);
printf(" 离散度: %.3f 秒\n", server->dispersion);
printf(" 最后联系: %s", ctime(&server->last_contact));
printf(" 可达: %s\n", server->reachable ? "是" : "否");
}

// 显示NTP客户端优势
printf("\n=== NTP客户端优势 ===\n");
printf("1. 高精度同步:\n");
printf(" ✓ 微秒级时间精度\n");
printf(" ✓ PPM级频率控制\n");
printf(" ✓ 毫秒级偏移调整\n");

printf("\n2. 智能选择:\n");
printf(" ✓ 多服务器支持\n");
printf(" ✓ 质量评分算法\n");
printf(" ✓ 动态服务器切换\n");

printf("\n3. 安全特性:\n");
printf(" ✓ 权限检查\n");
printf(" ✓ 错误处理\n");
printf(" ✓ 状态监控\n");

printf("\n4. 灵活配置:\n");
printf(" ✓ 可配置阈值\n");
printf(" ✓ 动态间隔调整\n");
printf(" ✓ 多种调整策略\n");

return 0;
}

int main() {
return demo_ntp_client();
}

adjtimex 使用注意事项

系统要求:

内核版本: 需要支持adjtimex的Linux内核

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

权限要求: 调整时钟需要root权限或CAP_SYS_TIME能力

架构支持: 支持所有主流架构

时钟状态:

TIME_OK: 时钟同步正常

TIME_INS: 即将插入闰秒

TIME_DEL: 即将删除闰秒

TIME_OOP: 闰秒处理中

TIME_WAIT: 等待同步

TIME_ERROR: 时钟错误

状态标志:

STA_PLL: 启用相位锁定环

STA_UNSYNC: 时钟未同步

STA_FREQHOLD: 频率保持

STA_PPSSIGNAL: PPS信号有效

STA_PPSJITTER: PPS抖动过大

STA_PPSWANDER: PPS频率漂移过大

STA_PPSERROR: PPS错误

调整模式:

ADJ_OFFSET: 调整时钟偏移

ADJ_FREQUENCY: 调整时钟频率

ADJ_MAXERROR: 设置最大误差

ADJ_ESTERROR: 设置估算误差

ADJ_STATUS: 设置状态标志

ADJ_TIMECONST: 设置时间常数

ADJ_SETOFFSET: 设置时间偏移

ADJ_MICRO: 微调模式

ADJ_NANO: 纳秒模式

ADJ_TICK: 调整时钟滴答

错误处理:

EPERM: 权限不足

EINVAL: 参数无效

EFAULT: 指针无效

EACCES: 访问被拒绝

EPERM: 操作被禁止

性能考虑:

调整频率: 避免过于频繁的时钟调整

调整幅度: 控制每次调整的幅度

系统负载: 考虑调整对系统性能的影响

监控开销: 减少监控带来的开销

安全考虑:

权限验证: 确保有适当的权限进行调整

参数验证: 验证所有输入参数的有效性

错误恢复: 准备适当的错误恢复机制

审计日志: 记录所有时钟调整操作

最佳实践:

渐进调整: 优先使用渐进调整而非步进调整

权限检查: 执行前检查是否具有足够权限

状态监控: 持续监控时钟状态和性能

错误处理: 妥善处理各种错误情况

日志记录: 记录所有重要的操作和状态变化

timex结构体详解

struct timex 结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct timex {
unsigned int modes; // 操作模式
long offset; // 时钟偏移(微秒)
long freq; // 频率调整(系统单位)
long maxerror; // 最大误差(毫秒)
long esterror; // 估算误差(毫秒)
int status; // 状态标志
long constant; // PLL时间常数
long precision; // 时钟精度(毫秒)
long tolerance; // 频率容忍度(ppm)
struct timeval time; // 当前时间
long tick; // 时钟滴答值
long ppsfreq; // PPS频率(系统单位)
long jitter; // PPS抖动(纳秒)
int shift; // PPS间隔宽度
long stabil; // PPS频率稳定度
long jitcnt; // PPS抖动计数
long calcnt; // PPS校准计数
long errcnt; // PPS错误计数
long stbcnt; // PPS稳定计数
int tai; // TAI偏移
int state; // 时钟状态
int :32; int :32; int :32; int :32;
};

相关函数和工具

系统调用:

1
2
3
4
5
6
7
8
#include <sys/timex.h>

// 时钟调整和查询
int adjtimex(struct timex *buf);

// 更现代的时钟调整接口
int clock_adjtime(clockid_t clk_id, struct timex *buf);

命令行工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 显示时钟状态
timex -p

# NTP时间同步
ntpd -q

# 手动时间同步
ntpdate pool.ntp.org

# 显示时间信息
date
hwclock

常见使用场景

1. NTP客户端:

1
2
3
4
5
6
// 调整时钟偏移
struct timex tx;
tx.modes = ADJ_OFFSET;
tx.offset = measured_offset_us;
adjtimex(&tx);

2. 系统时钟监控:

1
2
3
4
5
// 监控时钟状态
struct timex tx;
tx.modes = 0; // 查询模式
int state = adjtimex(&tx);

3. 高精度时间服务:

1
2
3
4
5
6
// 频率调整
struct timex tx;
tx.modes = ADJ_FREQUENCY;
tx.freq = ppm * 65536; // 转换为系统单位
adjtimex(&tx);

总结

adjtimex 是Linux系统中强大的时间管理函数,提供了:

精确控制: 微秒级时钟偏移调整

频率管理: PPM级频率控制

状态监控: 实时时钟状态查询

错误处理: 完善的错误处理机制

通过合理使用 adjtimex,可以构建高精度的时间同步系统。在实际应用中,需要注意权限要求、错误处理和性能优化等关键问题。

data-ad-format="auto" data-full-width-responsive="true">