前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux时间子系统(下)

Linux时间子系统(下)

作者头像
刘盼
发布2023-12-13 08:53:44
2890
发布2023-12-13 08:53:44
举报
文章被收录于专栏:人人都是极客人人都是极客

前言:

上一篇文章我们简单了解了一些关于时间的概念,以及Linux内核中的关于时间的基本理解。而本篇则会简单说明时钟硬件,以及Linux时间子系统相关的一些数据结构。

计算机里的时钟硬件

前文曾经提到过,内核时间子系统的实现也需要有硬件的支持。在计算机里一共有三类时钟硬件,分别是真时钟RTC(Real Time Clock)、定时器Timer、计时器Counter。

那生活中的场景举例,我们可以理解成RTC相当于是手表、座钟,定时器相当于是闹钟,计时器当然就是运动会中的计时器。

注意这是三类时钟硬件,而不是三个,某一类时钟可能有多个不同的硬件,某一个时钟硬件也可能实现多种不同的时钟类型。

计算机中还有其它的时钟类型,比如晶振时钟,是驱动CPU运行的周期信号,用来触发和同步CPU内部的操作,我们常说某CPU是多少GHz,就是说这个时钟晶振每秒向CPU发送多少信号。

晶振时钟一般在CPU内部,但有些嵌入式CPU的晶振在外部。时钟晶振在软件层不可见。还有一些设备也有自己的时钟,还有相应的驱动可以控制它。由于这些时钟都和时间子系统关系不大,所以本文中就不讨论它们了。

再具体点的话,我们以x86平台上的时钟举例说说:

  1. 真时钟RTC,在x86上的硬件实现也叫做RTC,和CMOS(计算机中有很多叫做CMOS的东西,但是是不同的概念,此处的CMOS是指BIOS设置保存数据的地方),是放在一起的。由于在关机后都需要供电,所以两者放在一起,由一个纽扣电池供电。所以有时候也会被人叫做CMOS时钟。
  2. 定时器Timer,在UP时代是PIT(Programmable Interval Timer),它以固定时间间隔向CPU发送中断信号。PIT可以在系统启动时设置每秒产生多少个定时器中断,一般设置是100,250,300,1000,这个值叫做HZ。到了SMP时代,PIT就不适用了,此时有多种不同的定时器。有一个叫做Local APIC Timer的定时器,它是和中断系统相关的。中断系统有一个全局的IO APIC,有NR_CPU个Local APIC,一个Local APIC对应一个CPU。所以在每个Local APIC都安装一个定时器,专门给自己对应的CPU发送定时器中断,就很方便。还有一个定时器叫做HPET(High Precision Event Timer),它是Intel和微软共同研发的。它不仅是个定时器,而且还有计时器的功能。HPET不和特定的CPU绑定,所以它可以给任意一个CPU发中断,这点和Local APIC Timer不同。
  3. 计时器Counter,RTC或者定时器虽然也可以实现计时器的目的,但是由于精度太差,所以系统都有专门的计时器硬件。计时器一般都是一个整数寄存器,以特定的时间间隔增长,比如说1纳秒增加1,这样两次读它的值就可以算出其中的时间差,而且精度很高。x86上最常用的计时器叫做TSC(Time Stamp Counter),是个64位整数寄存器。还有一个计时器叫做ACPI PMT(ACPI Power Management Timer),但是它是一个设备寄存器,需要通过IO端口来读取。而TSC是CPU寄存器,可以直接读取,读取速度就非常快。

Linux时间子系统的文件汇总

Linux kernel 时间子系统的源文件位于linux/kernel/time/目录下,基本包含如下:

这里面也包含几个重要的数据结构,接下来会分开说说:

Clocksource: 时钟源

clock source又被叫做时钟源,如果它的频率是10MHZ,就代表它每秒增加10M次,每增长一次我们称cycle加一,而且两次增长的时间间隔相同,通过这个性质,可以在两个时间点读取clock souce,相减得到一个差值,这个差值 / 频率就可以得到两个时间点的时间间隔。

设cycles:两个时间点的cycle差值,hz :每一纳秒的cycle值,time :两点之间的时间差(ns为单位)

所以可得:time = cycles/ hz

可以看到,通过cycles和hz做除法,可以很轻松的获得两时间点的具体时间差,但是落到代码中,就没那么简单了。

内核中因为效率或者兼容性问题,禁用了浮点数运算,如果用整数除法那么精度会受到影响,速度也不高,所以内核中用了乘法和移位运算的方式来实现上述公式,虽然也有误差,但运算速度很高。

内核计算时间差的公式:time = (cycles * mul) >> shift,计算mul和shift的过程如下:

下面详细解释一下这个计算过程:

上述代码,part2 很好理解,就是根据 mul = (time << shift) / cycle,做计算,part1可能比较难理解,意义如下:

time = (cycles * mul) >> shift,可以看到想要获取time,先要cycles * mul,如果cycles * mul溢出了,那计算结果就完全错误了,所以要对mul的值做限制,保证任何 可能的cycle的值 和mul值相乘都不会溢出,这就是part1的作用。

要达成不产生溢出的要求,首先要明确可能的cycle的值的范围,cycle一般是两次中断之间的时钟源计数差值。所以,求cycle值范围的问题,就转化成了,两次时钟中断的最长间隔。目前两次时钟中断的最长间隔被假设成了10分钟

为什么是10分钟?这是以下两个因素互相平衡做取舍的结果:

  1. 计算精度,从计算精度来看,mul的值肯定是越大越好,mul的值越大,得到的time值的精度也就越高,当然mul值越大,cycles的最大值就越小,这会影响到第二个因素"能耗".
  2. 能耗角度,这个和低功耗相关,因为如果系统处于idle线程的状态,会通过wfi进入低功耗状态,此时如果有中断来临,会让cpu退出,所以如果两次timer中断间隔时间太短,会增加功耗。

综上两点,内核选择 10分钟 这个值,作为两次时间中断时间间隔最大值,该逻辑可以在init_time_arch()中体现。

timer_device:时钟设备(也叫clock_event_device)

clocksrouce和clock_event_device之间的关系如上图所示。

在arm平台(其他平台应该也是类似)的设计中,硬件定时器设备和时钟源设备是配合使用的,硬件定时器可以设置时钟源到达何值时会产生一个中断。在smp系统中,为了减少处理器间的通信开销,基本上每个cpu都会具备一个属于自己的本地timer_device,独立地为该cpu提供时钟事件服务,smp中的每个cpu基于本地timer_device,建立自己的高精度定时器。

timekeeping模块

所谓timekeeping,如字面意思,就是让时间持续更新下去。

linux内核中维护了有三种时间概念:

  1. Wall time 现实时间。
  2. MONOTONIC time: 递增时间,从系统被启动时候开始计算,但不包含cpu低功耗状态的时间。
  3. Boot time: 递增的时间,在monotonic时间的基础上增加cpu的低功耗状态的时间。

上面三种时间通过 xtime变量计算,xtime会在系统启动的时候通过从rtc获取的值来初始化,之后通过每次时钟中断的时候,加上当前时间和上次中断产生时间的差值。

可能会有的疑问:为什么需要维护xtime,每次需要获取时间的时候读取rtc不就好了?但其实读取rtc也有缺点,比如:

  1. 读取rtc的效率不高,所以一般只在初始化的时候维护一次。
  2. rtc能提供的时间精度一般很低,最多就到毫秒级别,自己通过clock_source维护可以达到ns级别。

高精度定时器和低精度定时器:

传统的低精度定时器,是指让硬件定时器每隔固定时间(1ms或者10ms)产生一次中断,这种操作的默认语义就是允许产生ms级的延迟,这种时钟中断频率作为任务调度用途来说还可以接受,但是如果有更高精度需求的设备就完全无法满足了。

所以就出现了高精度定时器这种形式,它和低精度定时器的最大差别点在于:低精度是被动的等待下一次固定间隔的时钟中断的到来,而高精度定时器则会主动去设置硬件定时器,让它在第几个cycle上产生中断,从而满足自己的需求。

很自然的可以推测出来,如果要实现高精度定时器,那么必须保证硬件定时器支持one-shot模式,也就是可以以变化的中断频率出现。

同时为了满足 任务调度的需求和原来系统的对 周期性时钟中断的依赖,专门安排了一个hrtimer来按照(CONFIG_CPU_HZ)规定的频率来对硬件定时器进行设置,从而达到周期性产生时钟中断的效果。

相信通过两期的内容,可以帮助大家对计算机时间子系统有一个大概的了解,同时更好的理解hrtimer和timekeeping原理。显然关于Linux时间子系统的内容,还有很多可以深入挖掘,在此篇幅有限,就不做过多赘述了,也希望同样对操作系统感兴趣的小伙伴与我们一起交流。

部分内容出自:

CSDN博主「wmzjzwlzs」的原创文章,原文链接:https://blog.csdn.net/wmzjzwlzs/article/details/131617402

以及蜗窝科技 http://www.wowotech.net/timer_subsystem/time-subsyste-architecture.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人人都是极客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 计算机里的时钟硬件
  • Linux时间子系统的文件汇总
    • Clocksource: 时钟源
    • timer_device:时钟设备(也叫clock_event_device)
    • timekeeping模块
    • 高精度定时器和低精度定时器:
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档