Time And Date

来自osdev
跳到导航 跳到搜索

用户、文件系统、调度器、系统应用程序和一些用户应用程序都需要知道日期和/或时间,以达到不同的精度。 当时钟敲响某个时间时,一些应用程序需要启动、发送信号或发送消息。 为了提供这些服务,操作系统需要获取引导期间的当前时间,并维护该时间。

在一个系统中,“时间”有三个重要的细分: 人工时间、文件系统时间和调度程序时间片“ticks周期”。 人类的时间可以用秒(甚至是天,更长的时间)来衡量,其中“ticks”必须总是0.01秒或更小。 一个重要的设计决策是将这些时间格式分开,还是将它们合并在一起。

在内部维持时间

在操作过程中,一个典型的操作系统将使用本地硬件定时器来驱动自己的内部计时代码-- 通常被称为“系统时钟”。 在x86系统上,有两到四个计时器可用: PITRTCTSC或本地APIC(旧系统可能没有TSC或APIC)。 这些计时器可能会定期产生中断(甚至可能被轮询),以通知操作系统时间的流逝。 至少使用其中一个计时器可能是控制和生成调度程序时间片ticks所必需的。 在驱动程序中只需要很少的额外代码,就可以使用该计时器来更新系统时钟。 操作系统可能只需要知道所选计时器在一秒钟内ticks的频率,并保留其中一个进行计数-- 或者,系统时钟可以设计为以与所选计时器相同的速度ticks(译者注:ticks可以理解为周期,一次脉冲时长)。

然而,其中一些计时器能实现的精度仅为每天几秒钟以内,因此,即使在系统运行时,也可能需要定期将程序外部系统时钟基准化为参考。

也可以完全不使用计时器来更新系统时钟,而仅在系统尝试访问系统时钟时使用“准确”的外部参考。 根据所使用的参考时间来源,这种方法可能只精确到1秒,可能获取很慢,可能经常需要(尤其是对于文件系统访问),并且经常使用的格式不是操作系统想要的格式。

获取初始时间和日期

当电脑关机时,系统软件显然无法使用定时器更新内部时钟。 因此,当计算机重新启动时,操作系统需要使用其他方法来获取初始时间和日期。 实际上只有两种自动化方法。 如果这些方法失败,唯一存在的回退方法是要求用户输入日期/时间。

电池供电时钟

对于x86个人电脑,有一个特殊的“实时时钟”(RTC),它与系统的CMOS相结合。 它有自己的电池,所以当电脑关机时,它会继续运行,并且内存中的内容不会丢失。 有关从RTC读取时间和日期的信息,请参阅CMOS文章。

除了x86之外,几乎任何其他系统都会有某种电池供电的日期/时间时钟。

这样做的缺点是,电池最终总会耗尽,可能无法更换。 最好检查读取任何电池供电时钟的值是否是“正常”的。

网络时间

获取某种网络时间是实现跨机器准确性和一致性的一种优越方法。 缺点是最终用户的计算机可能(当前)未连接到网络,或者服务器可能会停机。

互联网

NTP是一个很好的协议,具有非常大的可由DNS自动选择的免费服务器。 它在端口123上使用UDP。 (Wikipedia article)

TIME协议非常简单: 将TCP套接字连接到服务器上的端口37 [http://tf.nist.gov/tf-cgi/servers.cgi NIST服务器),读取32位bigendian值(UTC 1900年1月1日凌晨后的秒数),然后关闭套接字。 这需要注意:它没有NTP那么准确;该值将在2036年滚动;还有正如服务器页面上所述,因为使用了TCP的完整机制,而只检索32位时间,这对服务器的带宽太浪费了。 Unix rdate命令使用TIME协议。

局域网

官方NTP软件可以很容易地配置为在局域网(或互联网)上为NTP服务。

编写一个时间服务器非常容易(这是服务器可以做到的最简单的事情),在局域网上使用TCP检索32位时间数据通常消耗不大。

编写自己的局域网协议也同样容易。 你可以使精细度和范围比NIST更好,但它可能很难达到NTP的精度。 至少有一个专业先例: Diskless Plan 9计算机从文件服务器获得时间。

内部操作系统时间格式

选择一种好的时间格式,并编写支持这种格式的代码,可能比最初看起来更复杂。 如上所述,在时间方面,人、文件系统和调度程序都有一些不同的需求。 它们在不同的时间尺度上运作。 您可以为每种格式创建单独的格式,或者,对于操作系统来说,以更高的精度维护单一的通用时间格式更有意义。 所有可能的选择都有缺点,要么复杂,要么浪费计算机资源。 需要注意的是,目前还没有标准,可能没有最佳选择。

人类时间尺度

在短期内,人类可以轻松应对秒数。 指定为小于秒的时间的时间格式在人类身上一部分来说是浪费。 事实上,向用户展示“太多”的准确性可能会让他们感到困惑,降低他们的工作效率。

另一方面,一旦一个文件(例如)存在一年以上,用户就不会再关心它创建的时间。 因此,从长远来看,用户将对以天为单位的时间更感兴趣。

这可能会支持以秒为单位的系统时间-- 或者是一种更灵活的格式,最初以秒为单位,然后切换到Julian Day Number之类的格式,持续更长的时间。 或者对于指定的时间间隔小于秒,但仅显示有限的可用实际时间信息的系统。

文件系统时间戳

几乎每个文件系统都使用预定义的时间格式。 再提一次,这里没有标准。 如果您的操作系统只支持一个文件系统,那么将您的操作系统时间格式与文件系统使用的时间格式相匹配可能是明智的,这样您就不需要进行转换了。 许多文件系统使用以秒为单位指定的时间格式,如果一些工具设计不当,这可能会导致“make”等实用程序出现“不完美”的结果。 当你设计一个操作系统时,你需要做出一个决定,是否要纵容他人糟糕的设计决策,但可以帮助他们的软件工作。

调度程序时间片

在多任务操作系统上,每个线程允许运行的时间长度通常基于一小段称为“时间片(Timeslices)”的时间。 这是操作系统的一个极其重要的功能,所以有一种计数器能在这个时间尺度上测量时间是非常重要的。 为操作系统提供通用时间格式的一个主要障碍是您的时间片长度往往是可变的,这可能会使您很难确定通用时间格式需要表示的最小时间长度。

历史日期

请记住,账目、数据库和其他程序可能需要存储上个世纪的日期,例如生日。 如果你拓展一下这方面的需求,甚至可以想象你想要存储许多世纪前的实际历史日期。 所以,再提一次,你可能需要为你的时间格式做出一些部分不太理想的选择,以支持这些功能。

时间格式示例

  • NIX时间格式记录自1970年初以来的32位秒。 该值在2100年左右过时,所以无法存储某些历史日期。

Windows时间格式使用自1601年1月1日起的64位无符号值,间隔为100纳秒(100 x 10-9秒)。 这种值大约在5万年后就会过时。 为什么是1601年? 也许它被认为是400年闰年周期的开始,这使得转换成日期变得更简单。 它也非常接近公历(大多数人使用的公历)的实际开始。 一个小小的缺点是,无论你想在哪里存储一个日期,你需要8字节的存储空间。 这可是很多地方。 你可以用它来存储文件版本号之类的东西。

BCOS使用的是非常相似的东西,除了从Y2K开始使用有符号的毫秒,而不是从1601开始使用无符号的100纳秒间隔。

“科学”格式

事实上,天文学中有一种时间测量方法,叫做儒略日数。 出于历史日历系统的原因,它从UTC正午(公元前4713年1月1日)开始测量时间,并使用浮点值以天为单位测量时间。 在一个非常缺乏标准的领域,至少,你可以认为它是一个标准。 因为它是一个标准,所以有代码可用于将JDN转换为日历日期。 在任何情况下,一旦你从任何时间格式中划分出秒和秒的分数,你就剩下“天”。 这意味着你的数字和儒略日数字之间的差异只是一天内的差值。 当然,可以截断JDN,并将其用作整数。 有关一些日期计算代码示例,请参阅Julian Day Number文章。


几点

一旦你决定了用什么格式来记录时间,决定你要记录的时间是很重要的。总的来说有 三种不同的时间:用户的“挂钟”时间、当地标准时间和UTC(“Universal Co-ordinated Time - 世界协调时间”)。

在任何时刻,UTC在世界各地都是一样的。 本地标准时间取决于您所在的时区(例如,我的本地标准时间始终为UTC+9.5小时)。 “挂钟时间”与当地标准时间相同,除非遇到夏令时 (例如,我的挂钟时间为UTC+9.5小时,夏季除外,此时为UTC+10.5小时)。

在互联网出现之前创建的操作系统是假设用户将电脑的电池时钟设置为墙上的时钟时间,这样电脑就可以轻松地作为电脑所有者的时钟使用。 操作系统认为,它自己可能需要负责调整计算机的实际电池供电时钟,以适应夏令时。 旧版本的Windows可以做到这一点。 当计算机双启动两个操作系统时,这可能会导致问题,这两个操作系统都希望在夏令时调整电池供电时钟(因此,它会意外地更改2小时,而不是1小时)。 参考 http://www.cl.cam.ac.uk/~mgk25/mswish/ut-rtc.html 来详细讨论这个问题。

然而,随着分布式计算的发展,需要同步多个计算机时钟。 显而易见的方法是使用UTC设置所有系统上的时钟。 在向用户显示时间之前,操作系统有责任将UTC转换为挂钟时间(包括夏令时的所有复杂性)。 Linux就是这样做的。 如果您的系统同时启动两种类型的操作系统,并且两种操作系统都试图读取电池供电的时钟,那么很明显,它们之间存在无法解决的冲突。

一些解决方案:

  • 不要使用电池供电的时钟
  • 使用电池供电的时钟是可选的
  • 允许超级用户以任何方式设置使用电池供电时钟的标志

愚蠢的做法是强迫时钟只能二选一。 作为一种不可行的指示,即使是Windows也可以以任何一种方式使用时钟。 (当然,它对非默认方式不太支持。)

复杂性

不管你做什么,你最终都需要将操作系统使用的时间格式转换成其他时间格式。 不幸的是,时间本身并不是一件简单的事情,时间格式之间的转换可能相当复杂(尤其是要完全准确地完成的话)。 此外,在不涉及转换的情况下维护多个操作系统时间也存在一些问题。 你可能需要注意的事情包括:

时区
主要用于在当地标准时间和UTC之间转换。 大多数开放源码软件都有一个数据库,因此每个时区都可以指定一个名称或位置(例如,“南澳大利亚阿德莱德”,而不是“UTC+9.5小时”)。 可在此处找到时区及其位置的地图:[1](1.23MB)
夏令时
这是一场噩梦。 有些国家对每个州有不同的时区规则,有些国家对一个州内的每个地区有不同的时区规则(美国尤其混乱)。 更糟糕的是,一些地区决定了他们每年要怎么做,因此不可能提前制定,而对于大多数地区来说,夏时制规则受制于政客的一时冲动。 对于一些操作系统,夏令时信息也与时区信息保存在同一个数据库中,这样用户就可以告诉操作系统它们在哪里,操作系统就可以从中找出合适的时区和夏令时规则。 对于过去的日期来说,夏令时可能特别有问题。
闰年
大家都知道,一年并不完全是365天。 格里高利闰年规则是,如果一年是4的倍数,那么它就是闰年,除非它恰好是一个新世纪,并且不能除以400。 例如,2004年、1996年和1968年是闰年,1700年、1800年和1900年不是闰年,但2000年、1600年和2400年是闰年。 这样可以使日期与季节同步。(译者注:因为地球自转周期一天和公转周期一年,一年时间不能精确被一天时间整除,一年略大于365天、小于366天。所以每隔一几年要补一天,而每100年又补多了所以不可以补,400年又需要补一天。否则积累下来,某具体日期会在公转周期一年内漂移,导致日期季节不同步。)
闰秒
由于标准机构、原子钟和引力的影响,一天的长度不完全是86400秒(平均每天比上一天稍长)。 为了克服这一点,增加了一个闰秒(大约每5年增加一秒)。 添加闰秒的时间列表可在 http://tf.nist.gov/pubs/bulletin/leapsecond.htm. 找到 这超出了任何计算机时钟的计时精度,但如果您想在格式中从一个时间减去另一个时间时创建非常准确的结果,这可能是一个问题。
日历
世界上大多数国家使用公历,但有些人不使用,有些人使用其他日历与公历结合使用。 如果你打算让你的操作系统国际化,或者如果你在1920年之前将时间格式转换为公历日期,那么你可能需要研究其他日历和(过去的日期)日历的历史。 一个很好的起点可以在 [2] 找到。
修正漂移
系统中的任何计时器都可能运行得很慢或很快,这可能被用户或操作系统检测到。 可能需要在每个计时器tick上加上或减去少量额外的时间。
准确度
不幸的是,个人电脑中的电子设备并不像它被认为的那样准确,而且随着时间的推移(不管其他什么),电脑的时间会变得不准确。 一些操作系统忽略了这个问题,允许用户随时更改或调整时间。 这会导致一些实用程序出现问题(如果您曾经收到来自“make”的“modification time in the future”错误消息,您就会知道原因)。 对于其他操作系统(通常是为从不关机的服务器设计的操作系统),有一种方法可以以更微妙的方式调整时间,通过许多微小的变化,而不是更大的突然变化。 例如*nix系统上的“adjtimex”实用程序。 然而,如果内部时钟错误的话,强制执行这些细微的变化可能会让用户难受。 我(网站ID:eekee)曾经遇到过这样一种情况:系统时钟关闭了8年,ntpd被打了补丁,忽略了允许突然变化的选项。 结果ntpd只能一点点做出微小的改变,导致将时间加速到正常速度的3倍左右,而因此计算机的时钟在2.5年内都不会正确! 如果操作系统在引导时没有从权威的外部源设置时间(有时你不能,因为没有网络),那么它必须在引导时查询用户,或者允许用户随时调整时间。 而有些用户会对每次开机都必须手动输入时间感到非常恼火。

另见

文章

论坛主题

外部链接