searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Linux的ntpd组件原理分析

2025-05-07 08:56:09
12
0

 基本概念

NTPNetwork Time Protocol,网络时间协议)是时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步。目前广泛使用的是19923月由RFC 1305定义的NTP3版本,以及最新的在20106月由RFC5905定义的NTPv4

而目前主流的NTP守护程序(ntpd)通常完整支持NTPv4,并且兼容NTPv3

 

 基本原理

NTP基于UDP报文进行传输,使用的UDP端口号为123。传输过程中,需要有一个设备作为时间服务器,其余设备将自己的时间与之同步,单向传输所需时间是1秒。

 关键流程

假设两个设备AB,通过NTP进行系统时钟同步,其中设备B作为时间服务器,那么它们的交互过程如下。

1 设备A发送NTP报文给B,报文带有离开A的时间戳10:00:00am,记作T1

2 设备B收到报文,在其中加入此刻的时间戳11:00:01am,记作T2

3 设备B返回报文给A,报文带有离开B的时间戳11:00:02am,记作T3

4 设备A收到响应报文,A此刻的时间戳为10:00:03am,记作T4

那么设备A就可以计算得到以下重要数据:

NTP报文往返时延delay = (T4 - T1) - (T3 - T2) = 2

设备A相对设备B的时间差offset = ((T2 - T1) + (T3 - T4)) / 2 = 1小时

于是,设备A可根据上述数据来调整自己的时钟,与设备B同步。

上述计算方法,可参考RFC1305文档定义的以下公式:

 

 

 配置方法

首先需要安装ntp服务,例如yum安装,执行yum install ntp即可。

配置文件是/etc/ntp.conf文件,执行man ntp.conf命令可查看ntpd服务的配置方法,其中常见的配置字段如下。

[ restrict ] 控制相关权限

语法:restrict ip mask x.x.x.x options

标蓝部分是需用户填写的参数,具体含义列举如下表。

参数

含义

ip

匹配此规则的主机IP,可以是default,即所有IP

x.x.x.x

子网掩码,例如255.255.0.0

options

权限配置,有以下选项:

ignore :关闭所有的 NTP 联机服务

nomodify:客户端不能更改服务端的时间参数

notrust :不信任未认证的客户端(不适用于NTP 4.2后的版本)

noquery :不支持客户端用ntpqntpc等命令查询ntp服务器

notrap :不提供trap远端登陆,拒绝提供陷阱服务

nopeer :阻止主机尝试与服务器对等

kod : 访问违规时发送 KoD

 

 

[ server ] 配置上游NTP服务器

作用:设置本地或远程的NTP服务器。与server类似的字段还有poolpeerbroadcastmanycastclient,它们分别应用于ntp的客户端/服务端模式、对等体模式、广播模式和组播模式。这里只列举最常用的server字段所对应的参数。

普通用法:设置远程服务器

server address [ key ] [ burst ] [ iburst ] [ version ] [ prefer ] [ minpoll ] [maxpoll ] [ true ] [ xmtnonce ]

参数

含义

address

服务器IP

key

取值有keyautokey,各自的含义如下。

key:指定收发包过程中的密钥ID,范围1~65536

autokey:使用自动生成的密钥

burst

NTP服务器是可连通状态时,连发8个包,间隔2秒。

目的是提高时钟同步的质量。

iburst

NTP服务器连不上时,连发8个包,间隔2秒。

目的是快速初始化ntp时钟同步相关的信息,并且配合ntpd-q选项。

version

指定NTP报文里的版本号,范围是1~4,默认值是4

prefer

与特殊用法的prefer相同

minpoll

与特殊用法的minpoll相同

maxpoll

与特殊用法的maxpoll相同

true

用于标记当前配置的NTP服务器是活跃的敲钟者(truechimer)

xmtnonce

指定NTP报文里的发送时间戳的随机值,仅对serverpool字段生效。

 

特殊用法:设置本地参考时钟

server 127.127.t.u [ prefer ] [ mode ] [ minpoll ] [ maxpoll ] 

参数

含义

t

表示本地的参考时钟类型,取值是一个整数。具体取值参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

u

表示单元编号,取值范围是0~3

prefer

作为一个常量附加到驱动提供的时间偏移,用于校准时钟

格式:定点十进制数,单位:秒

参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

mode

time1类似,由驱动自行解析。

minpoll

本地时钟的级别,一般为1

maxpoll

时钟参考源的标识符,可以是IP或者域名。

 

[ fudge ] 配置本地时钟

作用:调整时钟频率核时间偏移。

语法:fudge 127.127.t.u [ time1 ] [ time2 ] [ stratum ] [ flag1 ] ... [ flag4 ]

常见参数列举如下表。

参数

含义

t

表示本地的参考时钟类型,取值是一个整数。具体取值参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

u

表示单元编号,取值范围是0~3

time1

作为一个常量附加到驱动提供的时间偏移,用于校准时钟

格式:定点十进制数,单位:秒

参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

time2

time1类似,由驱动自行解析。

stratum

本地时钟的级别,一般为1

refid

时钟参考源的标识符,可以是IP或者域名。

flag1 ... flag4

取值范围是01,用来控制驱动自定义的特性。

 

例如设置本地时钟源层次为5

fudge  127.127.1.0 stratum 5

 

4.1  服务端配置

一个典型的服务端配置通常需要设定本地时钟,参见下方粗体标注。

driftfile /var/lib/ntp/drift

restrict default nomodify notrap nopeer noepeer noquery

restrict 127.0.0.1

restrict ::1

server 127.127.1.0 iburst local clock

includefile /etc/ntp/crypto/pw

keys /etc/ntp/keys

disable monitor

 

4.2  客户端配置

一个典型的客户端配置通常需要2个远端的NTP服务器,一个主服务器(下方绿字)和一个备用服务器(参见下方粗体标注)。

driftfile /var/lib/ntp/drift

restrict default nomodify notrap nopeer noepeer noquery

restrict 127.0.0.1

restrict ::1

server x.x.x.x iburst prefer minpoll 3 maxpoll 6

server x.x.x.x iburst minpoll 3 maxpoll 6

includefile /etc/ntp/crypto/pw

keys /etc/ntp/keys

disable monitor

 

 数据结构

涉及ntpd工作原理的主要数据结构是报文格式和工作模式。

5.1  struct pkt报文格式

NTP协议的报文格式如下图所示。

 

ntpd组件用struct pkt描述此报文格式,具体定义如下。

 

结构体字段

报文字段

描述

li_vn_mode

的位域 [1:0]

 

LI

(Leap Indicator)

长度为2比特,值为0b11时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。

 

li_vn_mode

的位域 [4:2]

 

VN

(Version Number)

长度为3比特,表示NTP的版本号,目前的最新版本为3

li_vn_mode

的位域 [7:5]

 

Mode

长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。

stratum

 

Stratum

系统时钟的层数,取值范围为116,它定义了时钟的准确度。层数为1的时钟准确度最高,准确度从116依次递减,层数为16的时钟处于未同步状态,不能作为参考时钟。

ppoll

 

Poll

轮询时间,即两个连续NTP报文之间的时间间隔。

precision

 

Precision

系统时钟的精度。

rootdelay

 

Root Delay

本地到主参考时钟源的往返时间。

rootdisp

 

Root Dispersion

系统时钟相对于主参考时钟的最大误差。

refid

Reference Identifier

参考时钟源的标识。

reftime

Reference Timestamp

系统时钟最后一次被设定或更新的时间。

org

Originate Timestamp

NTP请求报文离开发送端时发送端的本地时间。

rec

Receive Timestamp

NTP请求报文到达接收端时接收端的本地时间

xmt

Transmit Timestamp

应答报文离开应答者时应答者的本地时间。

exten

Authenticator

验证信息。

 

通过tcpdump抓包工具可抓取到如下典型的报文。

tcpdump -i enp0s3 udp port 123 -w capture.cap

 

持续抓取后,可发现本地机器与远程NTP服务器多次交互后,时差缩小。

 

 

5.2  MODE_xxxx(工作模式)

NTP支持5种工作模式,定义在include/ntp.h文件内。

 

它们的含义列举如下表。

模式枚举

宏定义

含义

模式1

MODE_ACTIVE

对等体模式的主动方

模式2

MODE_PASSIVE

对等体模式的被动方

模式3

MODE_CLIENT

客户端模式

模式4

MODE_SERVER

服务端模式

模式5

MODE_BROADCAST

广播模式

 

5.3  STA_UNSYNC标志

内核的time_status全局变量记录了NTP相关的重要标注,STA_UNSYNC标志就是其中之一,它的定义如下图,用于标记NTP时钟是否已同步。

 

【源码路径】

内核源码路径:include/uapi/linux/timex.h

用户使用头文件路径:/usr/include/linux/timex.h

 

其他相关标志】

STA_PLL:表示PLL update功能已使能

 

5.3.1  检测STA_UNSYNC

【用户态】

路径一:ntpq打印STA_UNSYNC标志

ntpq调用kerninfo()接口打印系统配置信息时,如果检测到STA_UNSYNC标志,就打印unsync

 

 

 

路径二:ntptime打印STA_UNSYNC标志

ntptime调用adjtimex系统调用后,获取到NTP状态,若有STA_UNSYNC标志,则把它打印出来。

 

【内核态】

sync_hw_clock()通过ntp_synced()检测STA_UNSYNC标志,如果此标志还在,说明NTP状态是未同步,则直接返回,不再继续设置RTC,反之则同步硬件时钟(RTC)

 

 

5.3.2  设置STA_UNSYNC

【用户态】

ntp组件主要是通过adjtimexsettimeofday系统调用设置STA_UNSYNC标志。

路径一:ntpd使用adjtimex系统调用

ntpd的全局变量kern_enable默认是1,它是决定ntpdloop_config()接口是否设置STA_UNSYNC标志的前提条件之一。

 

如果nptd检测到REQ_SET_SYS_FLAG或者REQ_CLR_SYS_FLAG命令码,则调用ntp_codes[ ]里注册的set_sys_flag()或者clr_sys_flag()接口,其内部的调用路径是:

set_sys_flag() -> setclr_flags() -> loop_config(LOOP_KERN_CLEAR, 0.0) -> ntp_adjtime()

clr_sys_flag() ---↗   

随后 ntp_adjtime()通过adjtimex系统调用,把STA_UNSYNC标志传给内核。

 

路径二:ntpd使用settimeofday系统调用

ntpd服务检测到时间差后会与NTP服务器交互,进行校时,该过程中会使用settimeofday系统调用。

 

 

 

【内核态】

内核里面设置STA_UNSYNC标志的,主要是2个系统调用:adjtimexsettimeofday和其他辅助接口。

* 路径1:处理adjtimex系统调用 *

内核调用__do_adjtimex()接口处理adjtimex系统调用,该接口除了实现校时功能以外,还会调用process_adj_status()设置STA_UNSYNC标志。前提条件是内核已记录了STA_PLL标志,且用户指定的标志不包含STA_PLL,参见下图代码。

 

 

 

* 路径2:处理settimeofday系统调用 *

内核调用do_settimeofday64()接口处理settimeofday系统调用,该过程中涉及STA_UNSYNC标志的主要流程如下。

1 调用timekeeping_update(),传入TK_CLEAR_NTP标志

2 timekeeping_update()检测到TK_CLEAR_NTP,就执行ntp_clear()接口

3 ntp_clear()设置STA_UNSYNC标志

注:与上述流程类似的接口列举如下。

timekeeping_inject_offset()

change_clocksource()

timekeeping_inject_sleeptime64()

 

* 路径3:内核初始化 *

内核启动阶段会设置STA_UNSYNC标志,该过程调用路径如下。

start_kernel() -> timekeeping_init() -> ntp_init() -> ntp_clear()

最终ntp_clear()就会设置STA_UNSYNC标志。

 

* 路径4:定时滴答里面设置STA_UNSYNC标志 *

当系统滴答的中断到来后,中断处理过程中会调用timekeeping_advance()接口。

timekeeping_advance()可能间接调用second_overflow()设置STA_UNSYNC标志。

 

 

 

 

5.3.3  清除STA_UNSYNC

 

【用户态】

ntpd有多处调用ntp_adjtime()接口,其内部是adjtimex系统调用,可对内核的time_status进行修改。调用ntp_adjtime()的相关接口列举如下:

(1) local_clock()接口内,若kern_enable已开启,根据其他某些条件不同,可能设置STA_PLLSTA_INSSTA_DEL中的某个标志,但最终附带的效果都是清除STA_UNSYNC标志。

(2) direct_freq()接口内,若kern_enable已开启,则清除所有标志

(3) set_freq()接口内,若kern_enable已开启,则清除所有标志

(4) loop_config()接口内,若条件允许,则设置STA_PLL,顺带清除STA_UNSYNC

 

 

【内核态】

内核的process_adj_status()接口内对 time_status做以下修改:

1 设置STA_RONLY

  由于STA_RONLY不包含STA_UNSYNC,所以相当于清除了STA_UNSYNC标志

2 设置用户通过adjtimex系统调用传来的标志

  如果用户传递的标志没有STA_UNSYNC,则STA_UNSYNC依然是被清除

 

 

 关键函数

 

6.1  rtc_set_time()设置RTC

路径一:系统调用ioctl

用户态通过ioctl()/dev/rtc 设备传递RTC_SET_TIME命令,内核的rtc_dev_ioctl()解析这个命令后,调用rtc_set_time()

路径二:系统调用adjtimex

内核收到adjtimex系统调用后,通过do_adjtimex()执行校时,调用链:

do_adjtimex() -> ntp_notify_cmos_timer()

内核ntp_notify_cmos_timer()注册了一个struct delayed_work结构体sync_work,其中带有sync_hw_clock(),它对rtc_set_time()的调用路径如下。

sync_hw_clock() -> sync_rtc_clock() -> rtc_set_ntp_time() -> rtc_set_time()

 

sync_hw_clock()的主要流程如下:

1 调用ntp_synced()检测STA_UNSYNC标志

  STA_UNSYNC标志还在,则说明NTP未同步,直接返回,否则继续处理

2 调用sync_cmos_clock()同步CMOS存储器上的时钟

  如果系统不支持同步CMOS的功能或者没有CMOS存储器,则继续

3 调用sync_rtc_clock()同步RTC芯片上的时钟(需开启CONFIG_RTC_SYSTOHC

其中sync_cmos_clock( )sync_rtc_clock()都会调用sched_sync_hw_clock()以设置一个sync_work工作项目,它按照约为11分钟的周期入队列(参见下图659秒)。

 

周期到来后,sync_hw_clock()将再次被调用。所以当ntpd同步了系统时钟(墙上时钟),内核将每11分钟左右同步一次硬件时钟,直到STA_UNSYNC被置起。

 

 

 

sync_hw_clock()内增加探测点测试,确认内核可以每隔约11分钟同步RTC,如下图所示。

 

从上图还可注意到设置RTC不是一蹴而就,而是尝试多次。这是因为rtc_set_ntp_time()有时会返回错误码(例如下图所示的EPROTO错误码71),此时内核会尽快重试,直到结果符合预期(返回值是0)。

 

 

6.2  rtc_read_time()读取RTC

内核启动过程中rtc_hctosys()读取RTC芯片的时钟,设置到内核的墙上时钟。

6.3  ktime_get_real_ts64()读取墙上时钟

ktime_get_real_ts64()tk_core.timekeeper.xtime_sec读取时间戳。

路径一:clock_gettime系统调用

用户态调用clock_gettime()后,do_clock_gettime()调用ktime_get_real_ts64()

路径二:gettimeofday系统调用

用户态调用gettimeofday()后,内核调用ktime_get_real_ts64()

6.4  do_settimeofday64()设置墙上时钟

do_settimeofday64()设置时间戳到tk_core.timekeeper.xtime_sec

注:tk_core结构体的成员是8字节的seq280字节的timekeeper

 

 选项分析

 

ntpd传入-x选项后,ntpd里面会检测到此选项。

 

随后执行以下调用路径:

getCmdOpts() -> loop_config(LOOP_MAX, 600) -> select_loop(FALSE)

这里select_loop()会对全局变量kern_enable赋值为FALSE,即0

 

 

 调试工具

ntpd组件中常用的调试方法是使用ntpd -d或者ntpq工具。

8.1  ntpd -d调试ntp通信流程

调试前须关停当前已有的ntpd服务。开始调试后,可看到以下关键信息。

1 当前系统时间、时钟精度和内核功能特性

 

2 配置文件的解析情况

 

3 监听123端口

 

4 与远程NTP服务器通信,最终成功校时

 

 

8.2  ntpdc交互界面

ntpdc大部分功能已被ntpq替代,但某些功能依然有用,例如修改STA_UNSYNC标志。

 

8.3  ntpq交互界面

ntpq继承了ntpdc的部分命令行工具,进入ntpq交互界面后,可以输入help查看支持执行哪些命令。

如果不想在交互界面执行命令,也可以用ntpq -c [命令]这种方式来执行。

 

8.4  ntpq查看NTP服务信息

输入ntpq -p后,ntpq将调用dogetpeers()获取各个peer的信息,并通过doprintpeers()NTP服务状态和时钟状态等信息打印出来。

 

一个典型的日志参见下图。

 

图中第一行的各个字段含义列举如下表。

字段

含义

remote

本机和上层ntpip或主机名,*表示良好且正在使用的NTP服务器+表示良好的候选NTP服务器。

refid

refid的含义取决于NTP服务器所处的stratum层级参考时钟源的主机。

对于stratum 0层,refid的值是KoD代码主要有以下情况:

(1) .INIT. 表示关联初始化

(2) .STEP. 表示间隔时长改变,大于125ms小于1000ms

对于stratum 1层,refid14字节的标识符,表示参考时钟:

(1) .WWVB. 标准时间无线电接收器

(2) .GPS.  USA GPS

(3) .OLEG.  北斗邦泰的时钟源产品,使用授时中心的NTP服务器ntp.ntsc.ac.cn就会看到此标识。

对于stratum 2-15层,refid是一个IP或者服务器节点名称。

对于stratum 16层,此时的NTP服务器是未同步的,不可使用。从实测情况看,refid有以下情况:

(1) KoD代码

(2) .XFAC. 根据ntp源码的peer_refresh_interface()接口,如果系统内原先与ntp服务器绑定的网络接口发生变化,那么调用peer_clear()清除crypto信息后,将会设置XFAC标识。

 

st

stratumNTP服务器的层级,这个值初始是16,表示NTP服务器未同步,不可使用。随着当前系统与NTP服务器逐渐建立通信,最终确定此NTP服务器的层级,那么st会被设置为0~15以内的值。

t

目标节点类型,该信息通过decodeaddrtype()接口解析后获取。

它的取值定义在ntpq/libntpq.h或者手册内,列举如下。

u : unicast / manycast client    s : symmetric ( peer )

b : broadcast client           A : manycast server

m : multicast client           B : broadcast server

p : pool source              M : multicast server

l : local (reference clock)       - : unknown type

 

 

when

前曾经同步过时间,单位:秒

poll

下次更新在多久,单位:秒

reach

已经多少次向上层ntp服务器要求更新

delay

网络延迟,单位:毫秒

offset

时间补偿,单位:毫秒

jitter

系统时间与bios时间差,单位:毫秒

 

 

8.5  ntpq查看kerninfo信息

 

这些信息在ntpq/ntpq-subs.ckerninfo()接口内被打印输出。

 

8.6  ntpq查看sysinfo信息

 

 

 

8.7  ntpq查看时间精度等信息

 

 

 编译安装

1 拉取代代码

2 如果代码来自github仓库,通常需要执行./bootstrap用于生成configure脚本

3 执行./configure进行配置

  如果需要静态编译,则配置./configure LDFLAGS="--static"

  如果需要支持-u选项,则增加配置--enable-clockctl

4 执行make进行编译

 

可能的问题:

1 找不到lynx命令

  解决:yum install lynx

2 提示无法确定ctx的大小

  解决:通常是github仓库的代码容易出错,换成已发布的源码会比较可靠

 

10  术语

名词

解释

PPM

parts per million1ppm = 0.0001%

NTP应用场景中,PPM常用来表征时钟质量。

TAI

International Atomic Time

PLL

Phase-Locked Loop,锁相环

 

0条评论
0 / 1000
彭乾恒
4文章数
1粉丝数
彭乾恒
4 文章 | 1 粉丝
彭乾恒
4文章数
1粉丝数
彭乾恒
4 文章 | 1 粉丝
原创

Linux的ntpd组件原理分析

2025-05-07 08:56:09
12
0

 基本概念

NTPNetwork Time Protocol,网络时间协议)是时间同步协议,用来在分布式时间服务器和客户端之间进行时间同步。目前广泛使用的是19923月由RFC 1305定义的NTP3版本,以及最新的在20106月由RFC5905定义的NTPv4

而目前主流的NTP守护程序(ntpd)通常完整支持NTPv4,并且兼容NTPv3

 

 基本原理

NTP基于UDP报文进行传输,使用的UDP端口号为123。传输过程中,需要有一个设备作为时间服务器,其余设备将自己的时间与之同步,单向传输所需时间是1秒。

 关键流程

假设两个设备AB,通过NTP进行系统时钟同步,其中设备B作为时间服务器,那么它们的交互过程如下。

1 设备A发送NTP报文给B,报文带有离开A的时间戳10:00:00am,记作T1

2 设备B收到报文,在其中加入此刻的时间戳11:00:01am,记作T2

3 设备B返回报文给A,报文带有离开B的时间戳11:00:02am,记作T3

4 设备A收到响应报文,A此刻的时间戳为10:00:03am,记作T4

那么设备A就可以计算得到以下重要数据:

NTP报文往返时延delay = (T4 - T1) - (T3 - T2) = 2

设备A相对设备B的时间差offset = ((T2 - T1) + (T3 - T4)) / 2 = 1小时

于是,设备A可根据上述数据来调整自己的时钟,与设备B同步。

上述计算方法,可参考RFC1305文档定义的以下公式:

 

 

 配置方法

首先需要安装ntp服务,例如yum安装,执行yum install ntp即可。

配置文件是/etc/ntp.conf文件,执行man ntp.conf命令可查看ntpd服务的配置方法,其中常见的配置字段如下。

[ restrict ] 控制相关权限

语法:restrict ip mask x.x.x.x options

标蓝部分是需用户填写的参数,具体含义列举如下表。

参数

含义

ip

匹配此规则的主机IP,可以是default,即所有IP

x.x.x.x

子网掩码,例如255.255.0.0

options

权限配置,有以下选项:

ignore :关闭所有的 NTP 联机服务

nomodify:客户端不能更改服务端的时间参数

notrust :不信任未认证的客户端(不适用于NTP 4.2后的版本)

noquery :不支持客户端用ntpqntpc等命令查询ntp服务器

notrap :不提供trap远端登陆,拒绝提供陷阱服务

nopeer :阻止主机尝试与服务器对等

kod : 访问违规时发送 KoD

 

 

[ server ] 配置上游NTP服务器

作用:设置本地或远程的NTP服务器。与server类似的字段还有poolpeerbroadcastmanycastclient,它们分别应用于ntp的客户端/服务端模式、对等体模式、广播模式和组播模式。这里只列举最常用的server字段所对应的参数。

普通用法:设置远程服务器

server address [ key ] [ burst ] [ iburst ] [ version ] [ prefer ] [ minpoll ] [maxpoll ] [ true ] [ xmtnonce ]

参数

含义

address

服务器IP

key

取值有keyautokey,各自的含义如下。

key:指定收发包过程中的密钥ID,范围1~65536

autokey:使用自动生成的密钥

burst

NTP服务器是可连通状态时,连发8个包,间隔2秒。

目的是提高时钟同步的质量。

iburst

NTP服务器连不上时,连发8个包,间隔2秒。

目的是快速初始化ntp时钟同步相关的信息,并且配合ntpd-q选项。

version

指定NTP报文里的版本号,范围是1~4,默认值是4

prefer

与特殊用法的prefer相同

minpoll

与特殊用法的minpoll相同

maxpoll

与特殊用法的maxpoll相同

true

用于标记当前配置的NTP服务器是活跃的敲钟者(truechimer)

xmtnonce

指定NTP报文里的发送时间戳的随机值,仅对serverpool字段生效。

 

特殊用法:设置本地参考时钟

server 127.127.t.u [ prefer ] [ mode ] [ minpoll ] [ maxpoll ] 

参数

含义

t

表示本地的参考时钟类型,取值是一个整数。具体取值参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

u

表示单元编号,取值范围是0~3

prefer

作为一个常量附加到驱动提供的时间偏移,用于校准时钟

格式:定点十进制数,单位:秒

参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

mode

time1类似,由驱动自行解析。

minpoll

本地时钟的级别,一般为1

maxpoll

时钟参考源的标识符,可以是IP或者域名。

 

[ fudge ] 配置本地时钟

作用:调整时钟频率核时间偏移。

语法:fudge 127.127.t.u [ time1 ] [ time2 ] [ stratum ] [ flag1 ] ... [ flag4 ]

常见参数列举如下表。

参数

含义

t

表示本地的参考时钟类型,取值是一个整数。具体取值参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

u

表示单元编号,取值范围是0~3

time1

作为一个常量附加到驱动提供的时间偏移,用于校准时钟

格式:定点十进制数,单位:秒

参考ntp文档的Reference Clock Drivers章节列举的各类驱动。

time2

time1类似,由驱动自行解析。

stratum

本地时钟的级别,一般为1

refid

时钟参考源的标识符,可以是IP或者域名。

flag1 ... flag4

取值范围是01,用来控制驱动自定义的特性。

 

例如设置本地时钟源层次为5

fudge  127.127.1.0 stratum 5

 

4.1  服务端配置

一个典型的服务端配置通常需要设定本地时钟,参见下方粗体标注。

driftfile /var/lib/ntp/drift

restrict default nomodify notrap nopeer noepeer noquery

restrict 127.0.0.1

restrict ::1

server 127.127.1.0 iburst local clock

includefile /etc/ntp/crypto/pw

keys /etc/ntp/keys

disable monitor

 

4.2  客户端配置

一个典型的客户端配置通常需要2个远端的NTP服务器,一个主服务器(下方绿字)和一个备用服务器(参见下方粗体标注)。

driftfile /var/lib/ntp/drift

restrict default nomodify notrap nopeer noepeer noquery

restrict 127.0.0.1

restrict ::1

server x.x.x.x iburst prefer minpoll 3 maxpoll 6

server x.x.x.x iburst minpoll 3 maxpoll 6

includefile /etc/ntp/crypto/pw

keys /etc/ntp/keys

disable monitor

 

 数据结构

涉及ntpd工作原理的主要数据结构是报文格式和工作模式。

5.1  struct pkt报文格式

NTP协议的报文格式如下图所示。

 

ntpd组件用struct pkt描述此报文格式,具体定义如下。

 

结构体字段

报文字段

描述

li_vn_mode

的位域 [1:0]

 

LI

(Leap Indicator)

长度为2比特,值为0b11时表示告警状态,时钟未被同步。为其他值时NTP本身不做处理。

 

li_vn_mode

的位域 [4:2]

 

VN

(Version Number)

长度为3比特,表示NTP的版本号,目前的最新版本为3

li_vn_mode

的位域 [7:5]

 

Mode

长度为3比特,表示NTP的工作模式。不同的值所表示的含义分别是:0未定义、1表示主动对等体模式、2表示被动对等体模式、3表示客户模式、4表示服务器模式、5表示广播模式或组播模式、6表示此报文为NTP控制报文、7预留给内部使用。

stratum

 

Stratum

系统时钟的层数,取值范围为116,它定义了时钟的准确度。层数为1的时钟准确度最高,准确度从116依次递减,层数为16的时钟处于未同步状态,不能作为参考时钟。

ppoll

 

Poll

轮询时间,即两个连续NTP报文之间的时间间隔。

precision

 

Precision

系统时钟的精度。

rootdelay

 

Root Delay

本地到主参考时钟源的往返时间。

rootdisp

 

Root Dispersion

系统时钟相对于主参考时钟的最大误差。

refid

Reference Identifier

参考时钟源的标识。

reftime

Reference Timestamp

系统时钟最后一次被设定或更新的时间。

org

Originate Timestamp

NTP请求报文离开发送端时发送端的本地时间。

rec

Receive Timestamp

NTP请求报文到达接收端时接收端的本地时间

xmt

Transmit Timestamp

应答报文离开应答者时应答者的本地时间。

exten

Authenticator

验证信息。

 

通过tcpdump抓包工具可抓取到如下典型的报文。

tcpdump -i enp0s3 udp port 123 -w capture.cap

 

持续抓取后,可发现本地机器与远程NTP服务器多次交互后,时差缩小。

 

 

5.2  MODE_xxxx(工作模式)

NTP支持5种工作模式,定义在include/ntp.h文件内。

 

它们的含义列举如下表。

模式枚举

宏定义

含义

模式1

MODE_ACTIVE

对等体模式的主动方

模式2

MODE_PASSIVE

对等体模式的被动方

模式3

MODE_CLIENT

客户端模式

模式4

MODE_SERVER

服务端模式

模式5

MODE_BROADCAST

广播模式

 

5.3  STA_UNSYNC标志

内核的time_status全局变量记录了NTP相关的重要标注,STA_UNSYNC标志就是其中之一,它的定义如下图,用于标记NTP时钟是否已同步。

 

【源码路径】

内核源码路径:include/uapi/linux/timex.h

用户使用头文件路径:/usr/include/linux/timex.h

 

其他相关标志】

STA_PLL:表示PLL update功能已使能

 

5.3.1  检测STA_UNSYNC

【用户态】

路径一:ntpq打印STA_UNSYNC标志

ntpq调用kerninfo()接口打印系统配置信息时,如果检测到STA_UNSYNC标志,就打印unsync

 

 

 

路径二:ntptime打印STA_UNSYNC标志

ntptime调用adjtimex系统调用后,获取到NTP状态,若有STA_UNSYNC标志,则把它打印出来。

 

【内核态】

sync_hw_clock()通过ntp_synced()检测STA_UNSYNC标志,如果此标志还在,说明NTP状态是未同步,则直接返回,不再继续设置RTC,反之则同步硬件时钟(RTC)

 

 

5.3.2  设置STA_UNSYNC

【用户态】

ntp组件主要是通过adjtimexsettimeofday系统调用设置STA_UNSYNC标志。

路径一:ntpd使用adjtimex系统调用

ntpd的全局变量kern_enable默认是1,它是决定ntpdloop_config()接口是否设置STA_UNSYNC标志的前提条件之一。

 

如果nptd检测到REQ_SET_SYS_FLAG或者REQ_CLR_SYS_FLAG命令码,则调用ntp_codes[ ]里注册的set_sys_flag()或者clr_sys_flag()接口,其内部的调用路径是:

set_sys_flag() -> setclr_flags() -> loop_config(LOOP_KERN_CLEAR, 0.0) -> ntp_adjtime()

clr_sys_flag() ---↗   

随后 ntp_adjtime()通过adjtimex系统调用,把STA_UNSYNC标志传给内核。

 

路径二:ntpd使用settimeofday系统调用

ntpd服务检测到时间差后会与NTP服务器交互,进行校时,该过程中会使用settimeofday系统调用。

 

 

 

【内核态】

内核里面设置STA_UNSYNC标志的,主要是2个系统调用:adjtimexsettimeofday和其他辅助接口。

* 路径1:处理adjtimex系统调用 *

内核调用__do_adjtimex()接口处理adjtimex系统调用,该接口除了实现校时功能以外,还会调用process_adj_status()设置STA_UNSYNC标志。前提条件是内核已记录了STA_PLL标志,且用户指定的标志不包含STA_PLL,参见下图代码。

 

 

 

* 路径2:处理settimeofday系统调用 *

内核调用do_settimeofday64()接口处理settimeofday系统调用,该过程中涉及STA_UNSYNC标志的主要流程如下。

1 调用timekeeping_update(),传入TK_CLEAR_NTP标志

2 timekeeping_update()检测到TK_CLEAR_NTP,就执行ntp_clear()接口

3 ntp_clear()设置STA_UNSYNC标志

注:与上述流程类似的接口列举如下。

timekeeping_inject_offset()

change_clocksource()

timekeeping_inject_sleeptime64()

 

* 路径3:内核初始化 *

内核启动阶段会设置STA_UNSYNC标志,该过程调用路径如下。

start_kernel() -> timekeeping_init() -> ntp_init() -> ntp_clear()

最终ntp_clear()就会设置STA_UNSYNC标志。

 

* 路径4:定时滴答里面设置STA_UNSYNC标志 *

当系统滴答的中断到来后,中断处理过程中会调用timekeeping_advance()接口。

timekeeping_advance()可能间接调用second_overflow()设置STA_UNSYNC标志。

 

 

 

 

5.3.3  清除STA_UNSYNC

 

【用户态】

ntpd有多处调用ntp_adjtime()接口,其内部是adjtimex系统调用,可对内核的time_status进行修改。调用ntp_adjtime()的相关接口列举如下:

(1) local_clock()接口内,若kern_enable已开启,根据其他某些条件不同,可能设置STA_PLLSTA_INSSTA_DEL中的某个标志,但最终附带的效果都是清除STA_UNSYNC标志。

(2) direct_freq()接口内,若kern_enable已开启,则清除所有标志

(3) set_freq()接口内,若kern_enable已开启,则清除所有标志

(4) loop_config()接口内,若条件允许,则设置STA_PLL,顺带清除STA_UNSYNC

 

 

【内核态】

内核的process_adj_status()接口内对 time_status做以下修改:

1 设置STA_RONLY

  由于STA_RONLY不包含STA_UNSYNC,所以相当于清除了STA_UNSYNC标志

2 设置用户通过adjtimex系统调用传来的标志

  如果用户传递的标志没有STA_UNSYNC,则STA_UNSYNC依然是被清除

 

 

 关键函数

 

6.1  rtc_set_time()设置RTC

路径一:系统调用ioctl

用户态通过ioctl()/dev/rtc 设备传递RTC_SET_TIME命令,内核的rtc_dev_ioctl()解析这个命令后,调用rtc_set_time()

路径二:系统调用adjtimex

内核收到adjtimex系统调用后,通过do_adjtimex()执行校时,调用链:

do_adjtimex() -> ntp_notify_cmos_timer()

内核ntp_notify_cmos_timer()注册了一个struct delayed_work结构体sync_work,其中带有sync_hw_clock(),它对rtc_set_time()的调用路径如下。

sync_hw_clock() -> sync_rtc_clock() -> rtc_set_ntp_time() -> rtc_set_time()

 

sync_hw_clock()的主要流程如下:

1 调用ntp_synced()检测STA_UNSYNC标志

  STA_UNSYNC标志还在,则说明NTP未同步,直接返回,否则继续处理

2 调用sync_cmos_clock()同步CMOS存储器上的时钟

  如果系统不支持同步CMOS的功能或者没有CMOS存储器,则继续

3 调用sync_rtc_clock()同步RTC芯片上的时钟(需开启CONFIG_RTC_SYSTOHC

其中sync_cmos_clock( )sync_rtc_clock()都会调用sched_sync_hw_clock()以设置一个sync_work工作项目,它按照约为11分钟的周期入队列(参见下图659秒)。

 

周期到来后,sync_hw_clock()将再次被调用。所以当ntpd同步了系统时钟(墙上时钟),内核将每11分钟左右同步一次硬件时钟,直到STA_UNSYNC被置起。

 

 

 

sync_hw_clock()内增加探测点测试,确认内核可以每隔约11分钟同步RTC,如下图所示。

 

从上图还可注意到设置RTC不是一蹴而就,而是尝试多次。这是因为rtc_set_ntp_time()有时会返回错误码(例如下图所示的EPROTO错误码71),此时内核会尽快重试,直到结果符合预期(返回值是0)。

 

 

6.2  rtc_read_time()读取RTC

内核启动过程中rtc_hctosys()读取RTC芯片的时钟,设置到内核的墙上时钟。

6.3  ktime_get_real_ts64()读取墙上时钟

ktime_get_real_ts64()tk_core.timekeeper.xtime_sec读取时间戳。

路径一:clock_gettime系统调用

用户态调用clock_gettime()后,do_clock_gettime()调用ktime_get_real_ts64()

路径二:gettimeofday系统调用

用户态调用gettimeofday()后,内核调用ktime_get_real_ts64()

6.4  do_settimeofday64()设置墙上时钟

do_settimeofday64()设置时间戳到tk_core.timekeeper.xtime_sec

注:tk_core结构体的成员是8字节的seq280字节的timekeeper

 

 选项分析

 

ntpd传入-x选项后,ntpd里面会检测到此选项。

 

随后执行以下调用路径:

getCmdOpts() -> loop_config(LOOP_MAX, 600) -> select_loop(FALSE)

这里select_loop()会对全局变量kern_enable赋值为FALSE,即0

 

 

 调试工具

ntpd组件中常用的调试方法是使用ntpd -d或者ntpq工具。

8.1  ntpd -d调试ntp通信流程

调试前须关停当前已有的ntpd服务。开始调试后,可看到以下关键信息。

1 当前系统时间、时钟精度和内核功能特性

 

2 配置文件的解析情况

 

3 监听123端口

 

4 与远程NTP服务器通信,最终成功校时

 

 

8.2  ntpdc交互界面

ntpdc大部分功能已被ntpq替代,但某些功能依然有用,例如修改STA_UNSYNC标志。

 

8.3  ntpq交互界面

ntpq继承了ntpdc的部分命令行工具,进入ntpq交互界面后,可以输入help查看支持执行哪些命令。

如果不想在交互界面执行命令,也可以用ntpq -c [命令]这种方式来执行。

 

8.4  ntpq查看NTP服务信息

输入ntpq -p后,ntpq将调用dogetpeers()获取各个peer的信息,并通过doprintpeers()NTP服务状态和时钟状态等信息打印出来。

 

一个典型的日志参见下图。

 

图中第一行的各个字段含义列举如下表。

字段

含义

remote

本机和上层ntpip或主机名,*表示良好且正在使用的NTP服务器+表示良好的候选NTP服务器。

refid

refid的含义取决于NTP服务器所处的stratum层级参考时钟源的主机。

对于stratum 0层,refid的值是KoD代码主要有以下情况:

(1) .INIT. 表示关联初始化

(2) .STEP. 表示间隔时长改变,大于125ms小于1000ms

对于stratum 1层,refid14字节的标识符,表示参考时钟:

(1) .WWVB. 标准时间无线电接收器

(2) .GPS.  USA GPS

(3) .OLEG.  北斗邦泰的时钟源产品,使用授时中心的NTP服务器ntp.ntsc.ac.cn就会看到此标识。

对于stratum 2-15层,refid是一个IP或者服务器节点名称。

对于stratum 16层,此时的NTP服务器是未同步的,不可使用。从实测情况看,refid有以下情况:

(1) KoD代码

(2) .XFAC. 根据ntp源码的peer_refresh_interface()接口,如果系统内原先与ntp服务器绑定的网络接口发生变化,那么调用peer_clear()清除crypto信息后,将会设置XFAC标识。

 

st

stratumNTP服务器的层级,这个值初始是16,表示NTP服务器未同步,不可使用。随着当前系统与NTP服务器逐渐建立通信,最终确定此NTP服务器的层级,那么st会被设置为0~15以内的值。

t

目标节点类型,该信息通过decodeaddrtype()接口解析后获取。

它的取值定义在ntpq/libntpq.h或者手册内,列举如下。

u : unicast / manycast client    s : symmetric ( peer )

b : broadcast client           A : manycast server

m : multicast client           B : broadcast server

p : pool source              M : multicast server

l : local (reference clock)       - : unknown type

 

 

when

前曾经同步过时间,单位:秒

poll

下次更新在多久,单位:秒

reach

已经多少次向上层ntp服务器要求更新

delay

网络延迟,单位:毫秒

offset

时间补偿,单位:毫秒

jitter

系统时间与bios时间差,单位:毫秒

 

 

8.5  ntpq查看kerninfo信息

 

这些信息在ntpq/ntpq-subs.ckerninfo()接口内被打印输出。

 

8.6  ntpq查看sysinfo信息

 

 

 

8.7  ntpq查看时间精度等信息

 

 

 编译安装

1 拉取代代码

2 如果代码来自github仓库,通常需要执行./bootstrap用于生成configure脚本

3 执行./configure进行配置

  如果需要静态编译,则配置./configure LDFLAGS="--static"

  如果需要支持-u选项,则增加配置--enable-clockctl

4 执行make进行编译

 

可能的问题:

1 找不到lynx命令

  解决:yum install lynx

2 提示无法确定ctx的大小

  解决:通常是github仓库的代码容易出错,换成已发布的源码会比较可靠

 

10  术语

名词

解释

PPM

parts per million1ppm = 0.0001%

NTP应用场景中,PPM常用来表征时钟质量。

TAI

International Atomic Time

PLL

Phase-Locked Loop,锁相环

 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0