Linux 使用 PTP 进行时间同步
概述
PTP(精确时间协议)是一种用于在网络中进行时钟同步的协议。当与硬件支持结合使用时,PTP 能够达到亚微秒的精度,这种精度远高于 NTP 协议。
PTP 时间同步协议的支持分为内核空间和用户空间两部分。在 Linux 系统中,PTP 协议的实际实现称为 LinuxPTP,它是 PTPv2 根据 Linux 的 IEEE 1588 标准实现的。在 LinuxPTP 软件包包括 ptp4l 和 phc2sys 用于时钟同步的程序,其中 ptp4l 程序实现了 PTP 边界时钟和普通时钟,支持硬件时钟同步和软件时间同步,硬件时间戳用于将 PTP 硬件时钟与主时钟同步,软件时间戳用于将系统时钟与主时钟同步。phc2sys 程序则用于将系统时钟同步到网卡上的 PTP 硬件时钟(PHC)。
准备工作
安装 LinuxPTP
首先,需要在 Linux 系统中安装 LinuxPTP。
Debian/Ubuntu 系统使用 apt 命令安装:
sudo apt install linuxptp
对于 RHEL/CentOS 6/7 系统,使用 yum 命令安装:
sudo yum install linuxptp
对于 RHEL/CentOS 8 和 Fedora 系统,使用 dnf 命令安装:
sudo dnf install linuxptp
如果是其他嵌入式 Linux 系统,或者你想安装最新版本的 LinuxPTP,可以采用源码编译安装方式:
sudo git clone git://git.code.sf.net/p/linuxptp/code linuxptp
cd linuxptp
sudo make
sudo make install
安装完成后可通过 ptp4l -v
命令检查是否安装成功:
$ ptp4l -v
1.92
检查网卡驱动
为了使用 PTP ,网络接口的内核网络驱动程序必须支持软件或硬件时间戳功能。除了驱动程序中存在的硬件时间戳支持之外,NIC 还必须能够在物理硬件中支持此功能。验证特定驱动程序和 NIC 的时间戳功能的最佳方法是使用 ethtool 查询接口。
例如:检查 eth0 网卡对硬件时间戳的支持
sudo ethtool -T eth0
执行上述命令后的输出结果可能如下:
Time stamping parameters for eth0:
Capabilities:
hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)
software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)
hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)
software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)
software-system-clock (SOF_TIMESTAMPING_SOFTWARE)
hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)
PTP Hardware Clock: 0
Hardware Transmit Timestamp Modes:
off (HWTSTAMP_TX_OFF)
on (HWTSTAMP_TX_ON)
one-step-sync (HWTSTAMP_TX_ONESTEP_SYNC)
Hardware Receive Filter Modes:
none (HWTSTAMP_FILTER_NONE)
all (HWTSTAMP_FILTER_ALL)
对于软件时间戳支持,参数列表应包括:
SOF_TIMESTAMPING_SOFTWARE
SOF_TIMESTAMPING_TX_SOFTWARE
SOF_TIMESTAMPING_RX_SOFTWARE
对于硬件时间戳支持,参数列表应包括:
SOF_TIMESTAMPING_RAW_HARDWARE
SOF_TIMESTAMPING_TX_HARDWARE
SOF_TIMESTAMPING_RX_HARDWARE
配置文件
ptp4l 配置文件
ptp4l 程序启动时会从 /etc/linuxptp/ptp4l.conf 文件读取配置选项,部分配置如下所示。
[global]
#
# Default interface options
#
network_transport UDPv4
delay_mechanism E2E
time_stamping hardware
tsproc_mode filter
delay_filter moving_median
delay_filter_length 10
egressLatency 0
ingressLatency 0
boundary_clock_jbod 0