网络时钟同步协议-- NTP, PTP

这篇文章介绍一下两个时钟同步的网络协议:NTP和PTP。
这里不涉及协议的原理和具体实现(想了解的可自行Google),重点是如何搭建起这两个服务。

1. NTP及PTP简介


NTP(Network Time Protocol)是用于不同计算机之间同步时钟的网络协议。
它的设计目标是使所有的互连的机器之间的时钟与UTC时间只相差若干毫秒

目前NTP协议已经是有第4版了,如果不需要了解NTP太多细节的话,看看这个wiki页面应该就足够了。需要注意的就是它有clock strata的概念。

PTP(Precision Time Protocol)看名字就知道是一个比NTP更精确的时钟同步协议了,PTP的设计目标是使机器之间的时钟偏差在sub-microsecond范围—这是wiki页面上提到的,有其他的地方说的是偏差若干微秒,本文搭建的环境中测量到的偏差也在微秒级别,没有到sub-microsecond级别。在使用PTP协议时,需要了解的主要概念点就是它的master/slave机制。

接下来我们就介绍我搭建NTP和PTP环境的过程,所用到的操作系统是CentOS6.5,内核版本是3.10。其他软件的版本会在用到时提及。

2. 搭建NTP服务


配置环境:两台服务器,一台做NTP服务器,一台做NTP的客户端。同时这两台机器都未联网。
NTP服务器地址:192.168.0.11
NTP客户端地址:192.168.0.22

A. 在192.168.0.11中启动NTP服务

B. 在192.168.0.22上启动NTP服务,选择192.168.0.11为NTP服务器

我的这两台实验机器是在同一个Rack的,结果显示差不多同步的偏差在30ms左右。
每个版本的ntpd配置文件可能有少许的差别,不过好在注释都做的不错,所以别的细节就不啰嗦了。

3. 搭建PTP服务


List of PTP implementations可以看到PTP的实现有很多很多种,可以是硬件实现的,可以是软件实现的也可以是软硬件结合实现的。本文中搭建的PTP服务是基于软件PTPd。如果没有特殊的硬件的话,这算是一种非常方便的方法了。

如果遇到任何问题,首先一定要看看help,使用-H选项的话还能看到非常详细的配置(虽然大多我可看不懂,不过不能不看,理解的越多,遇到的问题就会越少)。
如果log里面的信息看不懂,可以把代码下下来,一个grep搞定。

经测试,在我的机器上使用PTPD软件搭建的服务,时钟偏移的平均值能够达到5us左右。这个粒度基本能满足我们的需求了。

参考链接


Linux中的时钟准确度

本文来自戴尔软件开发高级工程师Stuart Hayes

一个关于计时的问题最近引起了我的关注。 有用户在 Dell 服务器上运行 Linux,并且发现时钟时间(Linux 所报告的)每天误差五秒以上,偏差比较明显。

我们首先想到的也许是运行 NTP,定期与真实世界同步操作系统的时钟。

但是这样的误差最初是如何产生的?

在Linux运行时,Linux 系统未使用实时时钟(real time clock - RTC) 硬件来计时。 Linux 通常在启动时从RTC 读取时间,然后从该点开始,Linux 使用另一个时钟来源(系统中的另一个计时器/计数器)来计时。

自第 2.6.18 版起,Linux 的内核就使用 CPU 时间戳计数器 (TSC) 作为首选的时钟来源(假设 TSC 以恒定频率运行,以新款的 Intel 服务器 CPU为例)。 但是,尽管读取速度很快且解析度很高,TSC 却不是一个已知的频率。 当内核使启动时,它需要使用频率已知的另一个计时器,对其进行校准。 这样的校准并非天衣无缝(即使用于校准的计时器也是如此),因此把 TSC 作为时钟来源会造成时间误差。

如果减少时间误差对您非常重要,而非若干微秒的调度延迟,则最好使用频率已知的计时器,如大多数 x86 服务器上提供的 HPET(高精度事件计时器)。 借助新内核上的参数"clocksource=hpet"可实现这一目标。 HPET 计时器以已知、固定的频率运行,Linux 内核可直接从计时器读取时间。

话说回来,尽管 HPET 的精度高于 TSC,运行 NTP 以在 Linux 中实现长期的时钟精度始终是很好的想法。
上述讨论仅适用于运行于裸机的 Linux。 在虚拟机中的问题略微复杂。

原文链接


Linux 中的时钟准确度

Linux时钟精度:毫秒?微妙?纳秒?

最近被内核时钟精度弄的很是郁闷。具体情况如下:

扫盲:1秒=1000毫秒=1000000微妙=1000000000纳秒

首先:linux有一个很重要的概念——节拍,它的单位是(次/秒)。2.6内核这个值是1000,系统中用一个HZ的宏表征这个值。同时有全局的jiffies变量,表征从开机以来经过的节拍次数(这里面还有故事,后面说,先记住这个)。当然还有wall_jiffies的墙上jiffies来表示从 07-01-1970 到现在的节拍数。每个节拍里面执行一次时钟中断。就是说,它的精度是毫秒。

接着:内核中还有一个变量xtime表征系统的实际时间(墙上时间),定义如下。其中xtime.tv_sec以秒为单位,存放从Unix祖宗定的纪元时间(19700701)到现在的秒数。xtime.tv_nsec以纳秒为单位,记录从上一秒开始经过的纳秒数。就是说,它的精度是纳秒。


最后:linux提供一个gettimeofday的系统调用,它会返回一个timeval的结构体,定义如下。解释同上,tv_sec代表墙上时间的秒,tv_usec表示从上一秒到现在经过的微秒数。就是说,它的精度是微妙。

精彩的来了:
1. 内核中的xtime会在每个时钟中断的时候被更新一次,也就是每个节拍更新一次。你妹!!每毫秒更新一次怎么能冒出来纳秒的精度??而且,内核还有可能丢失节拍。怎么能是纳秒??
2. 各种书上说,gettimeofday系统调用是读取的xtime的值。日,为啥读出来之后精度丢了?变成微妙了?

寻寻觅觅终于理清了故事:
针对问题1:在linux启动的时候,一个节拍的时间长度还会以纳秒为单位初始化到tick_nsec中,初始化值为999848ns,坑爹啊!不到一毫秒!节拍大约为1000.15Hz。靠!实际的节拍竟然不是准确的1000!所以在每个时钟中断通过wall_jiffies去更新xtime的时候得到的就是一个以纳秒为最小单位的的值。所以!xtime的粒度应该是不到1毫秒,也就是精度是不到1毫秒。

针对问题2:gettimeday系统调用的读xtime代码部分如下:

while部分使用了seg锁,只看中间的就好了。加了注释之后就很清晰了。由于节拍可能会丢失,所以lost是丢失的节拍数(不会很多)。至于计时器就比较麻烦了,timer可能有下面四种情况。

a. 如果cur_timer指向timer_hpet对象,该方法使用HPET定时器——Inter与Microsoft开发的高精度定时器频率至少10MHz,也就是说此时可提供真正的微妙级精度。
b. 如果cur_timer指向timer_pmtmr对象,该方法使用ACPI PMT计时器(电源管理定时器)平率大约3.58MHz,也就是说也可以提供真正的微妙级精度。
c. 如果cur_timer指向timer_tsc对象,该方法使用时间戳计数器,内置在所有8086处理器,每个CPU时钟,计数器增加一次,频率就是CPU频率,所以timer精度最高。完全可以胜任微妙级的精度。
d. 如果cur_timer指向timer_pit对象,该方法使用PIT计数器,也即是最开始提到的节拍计数,频率大概是1000Hz,此时显然不能提供精度达到微妙的时间。所以只有这种情况是假毫秒精度!

综上:如果使用gettimeofday系统调用,只要不要使用节拍计数器就可以保证达到微妙精度的时间(刨除进程上下文时间误差)。至于网上说的可以拿到纳秒精度的时间,看起来都是错的。除非通过修改内核,使用时间戳计数器实现。Over!

最后最后说一个事情:jiffies的定义的是4字节,你可能猜想它初始值是0。实际上,事实并非如此!linux中jiffies被初始化为0xfffb6c20,它是一个32位有符号数,正好等于-300 000。因此,计数器会在系统启动5分钟内溢出。这是为了使对jiffies溢出处理有缺陷的内核代码在开发阶段被发现,避免此类问题出现在稳定版本中。

参考《深入理解linux内核》

原文链接


Linux时钟精度:毫秒?微妙?纳秒?

ubuntu 17.10 gnome 3桌面隐藏顶栏

1.安装gnome-tweak-tool

2.安装hidetopbar扩展

3.重启电脑

4.启动gnome-tweak-tool

可以看到扩展-->Hide top bar扩展,开启即可隐藏顶栏。按键盘上的Windows图标键就会显示出来。如果还是没有隐藏,请点击设置按钮,在里面关闭智能隐藏

继续阅读ubuntu 17.10 gnome 3桌面隐藏顶栏

Windows 7系统上安装CentOS 7后通过修改grub2添加Windows 7启动引导项

按照以往的经验,先安装Windows 7之后,再装CentOS 7,那么CentOS 7应该会自动添加Windows 7启动项。但是安装完成后,发现启动项里没有Windows 7

我们需要手动添加Windows 7的启动项。

我们需要修改grub2的模版文件,然后执行grub2-mkconfig自动重建grub2引导列表。

至于set root=(hd0,1),还是set root=(hd0,msdos1),或者set root=(hd0,msdosX),请在启动列表中按下e键,进入grub rescue模式,执行ls命令列出分区,如果第一个启动不了,请逐个分区都试试。

参考链接


Centos7 + Windows7 双系统,通过修改grub2,重新添加 Win7 启动引导项

NVIDIA TX2 (Ubuntu 16.04)上安装CUDA 8.0

使用如下方式安装:

至于如何获取最新的NVIDIA TX2上使用的CUDA 8.0版本的下载地址,请访问Jetson Download Center,下载目前最新的JetPack 3.1。执行之后在存储目录下的repository.json中找到最新的下载链接即可。当然也可以让JetPack 3.1自动下载完成最新的CUDA 8.0版本到指定目录。

Ubuntu 16.04上使用crosstool-ng编译Nvidia TX2上使用的boost库

目前在Nvidia TX2上尝试安装ROS Kinetic的时候发现依赖libboost-all-dev,但是自带的源http://ports.ubuntu.com下面没有合适的源,尽管在universe源下面包含libboost-all-dev ,但是实际安装的时候发现安装不上。

因此尝试手工编译最新的boost安装包,建立自己的源。

首先,编译安装最新的crosstool-ng,如下:

生成的工具链在~/x-tools/aarch64-rpi3-linux-gnueabi目录下,编译的时候,指定编译工具即可。

安装编译依赖

接着,下载并编译最新的代码

注意,对于boost-1.58来说,当时的代码是没有检测ARM64的功能的,当年也没有ARM64的芯片。因此需要修改{boost_root}/libs/config/checks/architecture/arm.cpp里面的代码,增加ARM64的判断

调整为

参考链接


Linux下编辑ISO文件的方法

我们知道Windows下有类似UltraISO的这样的软件可以用来编辑ISO文件,可引导ISO也没有问题。近来在Linux下玩的比较多,正好要改个ISO,于是就开始找Linux下有没有类似的软件。

Ubuntu软件中心里有一款叫ISOMaster的软件,可以创建和修改ISO文件。

继续阅读Linux下编辑ISO文件的方法

OpenCV使用cv::imshow在子线程中更新图片不刷新

Ubuntu 16.04上测试OpenCV 3.2,遇到一个在子线程中更新图像,而界面不刷新的问题。

编译执行如下代码:

可以看到,上面的代码执行之后,界面并没有出现,即使出现也没有刷新。

这个问题是由于界面更新必须在主线程中执行,需要手工通知主线程。

修改后的代码如下:

也就是每次更新图片后手工调用代码

这个等候时间,只要大于0即可,比如:

参考链接


c++ opencv image not display inside the boost thread

在Makefile中检查gcc版本

问题描述

我想使用一些在旧的GCC版本中不可用的GCC警告开关(例如-Wtype-limits)。

有没有一个简单的方法来检查GCC版本?

最佳解决方案

我不会说容易,但是可以使用GNU makeSHELL函数来执行gcc --version命令,然后使用ifeq条件表达式来检查版本号,并适当地设置CFLAGS变量。

以下是一个简单的MakeFile示例:

注意:不存在ifgt语法。但是可以使用expr命令进行比较。

例子如下:

次佳解决方案

要将完整的3部分GCC版本(不仅第一位数字)转换成数字格式,适合比较(例如40701)使用

其中解决了任何版本部分中双数字版本号的可能性,以及gcc -dumpversion输出内容缺少3个部分的可能性(在一些较早的GCC版本中存在这种情况)。

所以要测试MakeFile中的版本,使用(注意$$里面的最后一个sed命令)

第三种解决方案

我刚刚遇到这个问题,我需要测试GCC的前两位数字,想要一个比上面sed过滤字符串更复杂的操作。我使用bc进行比较,因为它支持浮点数(expr将非整数(non-integers)都视为字符串):

如果在GCC 4.9之后发布了GCC 4.10,那么用sed处理是必要的:

参考链接


在Makefile中检查gcc版本?