Linux时间子系统(一) 基本概念

本文使用Q & A的方式来和大家以前探讨一下时间的基本概念

一、什么是时间?

这个问题实在是太复杂了,我都不知道这是一个物理学、宇宙学、还是热力学异或是哲学问题,我只是想从几个侧面来了解一下时间这个概念。本节内容都是我坐在公交车上瞎想的,对物理学有兴趣的人可以指出我的错误(一个搞linux kernel的人不会有太深刻的物理学知识的),对Linux时间子系统有兴趣的人还是忽略这个小节吧。

1、时间和空间以及相对性

有没有绝对时间的概念呢?时间是否是独立于一切存在的呢?相信有绝对时间的存在比较符合人类的思维,也就是说,有一根绝对时间轴存在,任何事件都可以投射在这个时间轴的某个点上,不论观察者处于什么状态,大家共享一个绝对的时间轴。在这样的时间框架下,同时的概念是绝对的,只要发生在时间轴的同一点上,那么两个事件就是同时发生。如果两个事件不同时发生,那么他们之间的间隔也是绝对的,所有感知到的时间间隔都是相同的。与之相对的是绝对空间的概念。任何事件发生的地点都可以对应到绝对空间中的一个位置点,两个位置点的长度是绝对的,任何观察者测量的结果都是一样的,无论观察者处于什么样的状态。

当然,了解狭义相对论的同学应该是可以建立起来自己的时空观,其实没有绝对的时间和空间(时间和空间是相对的而且是有关联的,组成四维时空),同时是相对的(相对于自己的参考系),事件A和事件B先后发生,位于不同的惯性参考系的观察者可以不同的观测结果,可能一个认为A和B相关1秒,而另外一个惯性参考系的人认为A和B之间相隔1.2秒。当然,我们研究linux kernel中的时间子系统,这里的参考系不可能是以光速级别在运动,因此传统的牛顿的时空观也是OK的,它是爱因斯坦时空观的一个特例。

2、时间的方向

时间有没有起点和终点?时间的箭头指向何方?什么是现在?什么是未来?时间是不是可逆的?有没有可能穿越?

根据热力学第二定律,对于一个孤立系统,其内部自发进行的与热相关的过程必然向熵增的方向进行。一个孤立系统如果不受外界任何影响,且系统最终处于平衡态,那么在平衡态时,系统的熵取最大值。从这个角度看,时间的方向就是熵增的方向,并且是不可逆的。

此外,当时间沿着它的方向流动的时候,是连续的,还是有一个基本的单位,一份一份的流动?我想如果能量的交换(热交换)是一份一份的,那么时间也是离散的。

3、感知时间

时间是和变化相关的,如果一切处于静止态,没有位移、没有能量的变化,那么时间还有存在的意义吗?物体从一个位置A移动到了另外一个位置B,观察者通过感知位置的变化来感知时间,判断事件的先后顺序。当一切处于静止状态,没有任何的运动,各种恒星和行星都处于静止态,电子不再围绕原子核进行高速运动,宇宙中的所有的系统没有能量的交换,那么时间也就停止了,或者说不存在了。本质上,宇宙的状态和时间轴应该是一一对应的,如果宇宙的状态只有一种,那么时间轴就停止在一个点上,不再流动了。

二、度量时间

1、如何定义秒?

在定义秒这个术语的时候是有两个标准的:

(1)以铯133的振荡频率来定义秒。

(2)依据地球自转和公转来定义秒。

毫无疑问,第二种方法是符合人类习惯的,大家已经习惯了一天24个小时,每个小时60分钟,一分钟包括60秒数。我们可以用平均太阳日的1/86400来定义,也可以用绕太阳轨道公转一年的时间来定义秒,但是这种基于天体运动而定义的秒其实是不精准的,因为地球的自转和公转的时间不是一个恒定的值。由此,以铯133的振荡频率这样的物理属性来定义秒实际上可以赋予秒一个恒定的时间长度。

对于linux kernel而言,它采用了第一种定义的秒,并且参考点(linux epoch)也是UTC时间,UTC这个时间标准就是采用第一种方式来定义时间的。历史上,POSIX标准曾经将时间参考点设定为1970年1月1日0点0分0秒(GMT),GMT就是采用第二种方法定义时间的,后面修正为UTC时间。

定义了秒之后,我们可以进一步细分为毫秒、微秒、纳秒等,也可以组织成分钟、小时、日、月、年等概念。

2、如何处理闰秒(leap seconds)?

正是因为UTC时间和我们日常使用的日历时间有差异,因此才有闰秒(leap second)这个概念。虽然你UTC时间使用严格的定义,使用原子钟作为参考,秒的概念被精准的定义,但是,它还是要向现实低头,因为广大人民群众喜闻乐见的是和地球自转和公转相关的日历时间。为了让UTC时间和人民群众的日历时间保持一致,在适当的时间点(误差累积到1秒的时候)对UTC时间进行增加或者减少一秒的的操作,这就是闰秒的概念了。

由于目前地球的自转一直在变慢,因此我们的日历时间(GMT)是在变长的,因此目前的闰秒调整都是+1秒的操作。未来是否地球自转会变快,谁知道呢?

对于linux kernel而言,虽然epoch是UTC时间,不过内核不考虑闰秒问题。对于一个天文学意义上的较短的时间段(例如15个月)UTC和GMT是基本相同的,大部分的应用其实也基本是可以正常工作的。对这个问题的解决来自具体的应用场景和系统实现,内核只是支持了NTP协议,可以和外部的时间服务器进行同步,而外部的那些时间同步服务器都是支持UTC的,让它们去考虑闰秒吧,linux kernel只需要保持和它们同步就OK了。

3、如何处理闰年

科学必须用来指导人类的生产和生活,否则就没有意义了。在精确定义了时间的度量后,我们可以用科学的方法来组织时间。例如:地球绕太阳公转的周期是365天6小时9分钟10秒(或者说365.2564天)。不是整数天则需要特别处理,例如:考虑平年365天,闰年366天,每四年有一个闰年。如果这样的话,0.2564 x 4 = 1.0256,也就是说,这样的处理就多算了0.0256天,怎么办?累积长了也是一个不小的误差。根据计算,每100次闰年计算(周期400年),就多了2.56天,因此,定义在能被100整除的年份中(如果计算周期是400年,那么有4个这样的年份),选取3个不进行闰年,只有能被400整除的年份才闰年。这样计算的结果就是每400年就少算了0.44天,这样的计算可以无穷无尽计算下去。

4、如何定义基准?

时间有绝对和相对的概念。例如1小时候我们就去吃饭。这里就是一个相对时间的概念,1小时是相对当前时间点而言。1949年10月1日,中华人民共和国成立,这里的时间就是绝对时间。当然,实际上,这里的绝对时间也是相对时间,只不过所有地球上的人都使用同一个基准点的时候,这个时间表示就是一个普遍适用的绝对时间了。

对于一个系统而言,需要定义一个epoch,所有的时间表示是基于这个基准点的,对于linux而言,采用了和unix epoch一样的时间点:1970年1月1日0点0分0秒(UTC)。NTP协议使用的基准点是:1900年1月1日0点0分0秒(UTC)。GPS系统使用的基准点是:1980年1月6日0点0分0秒(UTC)。每个系统都可以根据自己的逻辑定义自己epoch,例如unix epoch的基准点是因为unix操作系统是在1970年左右成型的。

三、Linux kernel中和时间相关的基本概念

1、系统时钟(system clock)

我们在之前的文章中说过,时间就像是一条没有起点、没有终点的线段,除了要给出度量单位(例如:秒),还有给出参考点。对于linux kernel而言,这个参考点就是Linux Epoch。所谓linux Epoch其实就是1970年1月1日0点0分0秒(UTC)的时间点。虽然人类喜欢年、月、日、时、分、秒这些概念,不过对于计算机,更喜欢使用当前的时间点到linux epoch的秒数。一个符合POSIX标准的系统必须提供系统时钟,以不小于秒的精度来记录到linux epoch的时间值。

内核支持的system clock定义如下:

#define CLOCK_REALTIME            0

#define CLOCK_MONOTONIC            1

……

CLOCK_REALTIME是描述真实世界的时钟(这里的英文realtime有两个意思,一个表示真实的时间的意思,另外一个是实时性的意思,其实不止中文有歧义,英文也是一样的,需要根据上下文判断,我们这个场景当然不是讲实时性),就类似挂在墙上的钟表一样,告知人类当前的时间。当然,家里的钟表你当然可以按照你的意愿向前或者向后调整。CLOCK_MONOTONIC是一个禁止人为设定的真实世界的时钟,你没有办法设定它,但是可以通过NTP协议进行调整。更多的system
clock ID会在后续的文章中给出详细定义

2、 broken-down POSIX time

在time line上以linux
epoch为参考点,用到参考点的秒数来表示timeline上的某个时间点的方法对于计算机而言当然是方便的,不过对于使用计算机的人类而言,我们更习惯broken-down
time,也就是将那个冰冷的到参考点的秒数值分解成年月日时分秒。在内核中用下面的数据结构表示(注释非常详细,这里不再赘述):

struct tm {

/*

* the number of seconds after the minute, normally in the range

* 0 to 59, but can be up to 60 to allow for leap seconds

*/

int tm_sec;

/* the number of minutes after the hour, in the range 0 to 59*/

int tm_min;

/* the number of hours past midnight, in the range 0 to 23 */

int tm_hour;

/* the day of the month, in the range 1 to 31 */

int tm_mday;

/* the number of months since January, in the range 0 to 11 */

int tm_mon;

/* the number of years since 1900 */ -----以NTP epoch为基准点

long tm_year;

/* the number of days since Sunday, in the range 0 to 6 */

int tm_wday;

/* the number of days since January 1, in the range 0 to 365 */

int tm_yday;

};

3、不同精度的时间表示

传统的unix使用了基于秒的时间定义,相关的数据结构是time_t:

typedef long        time_t;

time_t是POSIX标准定义的一个以秒计的时间值。例如大家都比较熟悉的time函数,可以获取一个从linux
Epoch到当前时间点的秒值,该函数的返回值类型就是time_t的。如果time_t被实现成一个signed 32-bit
integer,那么实际上在2038年的时候就会有溢出问题。

随着应用的发展,秒精度已经无法满足要求,因此出现了微秒精度的时间表示:

struct timeval {

time_t        tv_sec;        /* seconds */

suseconds_t    tv_usec;    /* microseconds */

};

struct timeval 的概念和time_t是一样的,只不过多了一个微秒的成员,将时间的精度推进到微秒级别。在计算当前时刻到epoch时间点的微秒数值的时候可以使用公式:tv_sec x 10^6 + tv_usec。

由于实时应用程序的需求,POSIX标准最终将时间精度推进到纳秒,纳秒精度的时间表示如下:

struct timespec {

time_t    tv_sec;            /* seconds */

long        tv_nsec;        /* nanoseconds */

};

根据POSIX标准,timeline上一个时间点的值用struct timespec来表示,它应该最少包括:

成员数据类型 成员的名字 描述
time_t tv_sec 该时间点上的秒值,仅在大于或者等于0的时候有效。
long tv_nsec 该时间点上的ns值,仅在大于或者等于0,并且小于10^9纳秒的时候有效。

struct timespec和struct timeval概念类似,这里不再赘述。

原文地址:https://www.cnblogs.com/alantu2018/p/8448295.html

时间: 2024-08-13 21:36:37

Linux时间子系统(一) 基本概念的相关文章

Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)

在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由一个周期时钟提供,不管系统中的clock_event_device是工作于周期触发模式,还是工作于单触发模式,也不管定时器系统是工作于低分辨率模式,还是高精度模式,内核都竭尽所能,用不同的方式提供周期时钟,以产生定期的tick事件,tick事件或者用于全局的时间管理(jiffies和时间的更新),或者用于本地cpu的进程统计.时间轮定时器框架等等.周期性时钟虽然简单有效,但是也带来了一些缺点,尤其在系统的功耗上,因为就算系统目

Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

上一篇文章,我介绍了传统的低分辨率定时器的实现原理.而随着内核的不断演进,大牛们已经对这种低分辨率定时器的精度不再满足,而且,硬件也在不断地发展,系统中的定时器硬件的精度也越来越高,这也给高分辨率定时器的出现创造了条件.内核从2.6.16开始加入了高精度定时器架构.在实现方式上,内核的高分辨率定时器的实现代码几乎没有借用低分辨率定时器的数据结构和代码,内核文档给出的解释主要有以下几点: 低分辨率定时器的代码和jiffies的关系太过紧密,并且默认按32位进行设计,并且它的代码已经经过长时间的优化

Linux时间子系统(十七) ARM generic timer驱动代码分析

一.前言 关注ARM平台上timer driver(clocksource chip driver和clockevent chip driver)的驱动工程师应该会注意到timer硬件的演化过程.在单核时代,各个SOC vendor厂商购买ARM core的IP,然后自己设计SOC上的peripherals,这里面就包括了timer的硬件.由于没有统一的标准,各个厂商的设计各不相同,这给驱动工程师带来了工作量.然而,如果仅仅是工作量的话就还好,实际上,不仅仅如此.linux的时间子系统要求硬件t

Linux时间子系统(十五) clocksource

一.前言 和洋葱一样,软件也是有层次的,内核往往需要对形形色色的某类型的驱动进行抽象,屏蔽掉其具体的特质,获取该类驱动共同的逻辑,而又根据这些逻辑撰写该类驱动的抽象层.嵌入式系统总是会提供timer的硬件block,软件需要对timer硬件提供的功能进行抽象:linux kernel将timer类型的硬件抽象成两个组件,一是free running的counter,另外一个是指定的counter值上产生中断的能力.本文主要描述第一个组件,在内核中被称作clock source. 二.什么是clo

Linux时间子系统专题汇总

DroidPhone关于Linux时间子系统专题: http://blog.csdn.net/DroidPhone/article/category/1263459 Linux时间子系统之一:clock source(时钟源) Linux时间子系统之二:表示时间的单位和结构 Linux时间子系统之三:时间的维护者:timekeeper Linux时间子系统之四:定时器的引擎:clock_event_device Linux时间子系统之五:低分辨率定时器的原理和实现 Linux时间子系统之六:高精

Linux时间子系统(三) 用户空间接口函数

一.前言 从应用程序的角度看,内核需要提供的和时间相关的服务有三种: 1.和系统时间相关的服务.例如,在向数据库写入一条记录的时候,需要记录操作时间(何年何月何日何时). 2.让进程睡眠一段时间 3.和timer相关的服务.在一段指定的时间过去后,kernel要alert用户进程 本文主要描述和时间子系统相关的用户空间接口函数知识. 二.和系统时间相关的服务 1.秒级别的时间函数:time和stime time和stime函数的定义如下: #include <time.h> time_t ti

Linux时间子系统(十三) Tick Device layer综述

一.前言 时间子系统中的tick device layer主要涉及kernel/time/tick-*相关的文件,本文的主要内容就是从high level层次(不纠缠在具体的每行代码)描述tick device layer的运作逻辑. 如果说每个.c文件是一个模块的话,我们可以首先简单描述tick device layer的各个模块.tick-common.c描述了tick device的一些通用操作,此外,该文件还包括了周期性tick的代码.想要让系统工作在tickless mode(更准确应

linux时间子系统(七)

简单介绍linux下的时间子系统.包括clocksource,timekeeper和定时器的内容. 3.2 高精度定时器  随着内核的不断升级和硬件的不断发展,由于低精度定时器有一定的局限性,内核从2.6.16开始加入了高精度定时器架构.在实现方式上,高精度定时器的实现代码几乎没有借用低精度定时器的数据结构和代码,原因有以下几点: 1 低精度定时器的代码和jiffies的关系太过紧密,并且默认按照32为进行设计,如果要基于它来实现高精度时钟,必然会打破原有的time wheel的概念,而且会引入

Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()

我们已经在前面几章介绍了低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子系统,在时间子系统中提供了一些用于延时或调度的API,例如msleep,hrtimer_nanosleep等等,这些API基于低分辨率定时器或高精度定时器来实现,本章的内容就是讨论这些方便.好用的API是如何利用定时器系统来完成所需的功能的. /**************************************************************************************