关于linux内核中使用的时钟

linux内核里面用了很多的时钟,其实这些时钟的用处无外乎就几种:1.作为心跳,中断cpu;2.使得用户可以获取当前时间;3.实时测量;4.定时 服务。知道了使用目的后,我们来看看到底有哪些时钟供我们选择使用:关于这些时钟的概念,网上已经存在不少文章了,为了不重复(浪费互联网空间),咱就先来个引用。
/*摘录开始:http://blog.chinaunix.net/u1/55599/showart.php?id=1179259
1):实时时钟(RTC)
该时钟独立于CPU和其它芯片.即使PC断电,该时钟还是继续运行.该计时由一块单独的芯片处理,并把时钟值存放CMOS.该时间可参在IRQ8上周期性的产生时间信号.频率在2Hz ~ 8192Hz之间.但在linux中,只是用RTC来获取当前时间.
2):时间戳计时器(TSC)
CPU附带了一个64位的时间戳寄存器,当时钟信号到来的时候.该寄存器内容自动加1
3):可编程中断定时器(PIC)
该设备可以周期性的发送一个时间中断信号.发送中断信号的间隔可以对其进行编程控制.在linux系统中,该中断时间间隔由HZ表示.这个时间间隔也被称为一个节拍(tick).
在 ./include/asm-i386/param.h 定义
10#ifndef HZ
11#define HZ 100
12#endif
4):CPU本地定时器
在处理器的本地APIC还提供了另外的一定定时设备.CPU本地定时器也可以单次或者周期性的产生中断信号.与上次描述的PIC相比.它有以下几点的区别:
APIC本地计时器是32位.而PIC是16位.由此APIC本地计时器可以提供更低频率的中断信号
本地APIC只把中断信号发送给本地CPU.而PIC发送的中断信号任何CPU都可以处理
APIC定时器是基于总线时钟信号的.而PIC有自己的内部时钟振荡器
5):高精度计时器(HPET)
在linux2.6中增加了对HPET的支持.HPET是一种由intel开发的新型定时芯片.该设备有一组寄时器,每个寄时器对应有自己的时钟信号,时钟信号到来的时候就会自动加1.一个 hpet包括了一个固定频率的数值增加的计数器以及3到32个独立的计时器,这每一个计时器有包涵了一个比较器和一个寄存器(保存一个数值,表示触发中断时机)。每一个比较器都比较计数器中的数值和寄存器中的数值,当这两个数值相等时,将产生一个中断
实际上,在intel多理器系统与单处理器系统还有所不同:
在单处理系统中.所有计时活动过由PIC产生的时钟中断信号触发的
在多处理系统中,所有普通活动是由PIC产生的中断触发.所有具体的CPU活动,都由本地APIC触发的.
*/摘录完毕
好了,上面对这么多种时钟的概念已经阐述的很清楚了,那么他们之间到底有何联系呢?幸运的是,在2.6.21内核以后,一切变得不言自明了,2.6.21以上内核将时钟框架进行了进一步的抽象整理,抽象出了两个概念:
struct clocksource :对硬件时钟设备的抽象,描述时钟源,强调静态事物,源头
struct clock_event_device :时钟的事件的抽象,描述硬件时钟中断发生时要执行的动作,强调在时钟源上面的动态方面。
这两个概念被抽象出来以后,我们就可以用面向对象的思路来分析了。仔细看看这两个概念,它们的偶合性极低,但是还是有联系的,比如如果你将 clock_event_device设置成one-shot,而clocksource确是一个精度及其低的时钟源,那么系统的响应将会相当慢,它们实 际上会影响到对方。
仔细研究一下上面的众多时钟,有的可以产生中断,有的仅仅是一个计数器,有的...,那么我们怎么读时钟源啊,不要急,每个clocksource都有一个回调函数用来读取时钟源当前值:

struct clocksource {

         char *name;

         struct list_head list;

         int rating;

         cycle_t (*read)(void);//这就是读取回调函数

         cycle_t mask;

         u32 mult;

         u32 shift;

         unsigned long flags;

         cycle_t (*vread)(void);

         void (*resume)(void);

...

         /* timekeeping specific data, ignore */

         cycle_t cycle_interval;

         u64     xtime_interval;

         /*

          * Second part is written at each timer interrupt

          * Keep it in a different cache line to dirty no

          * more than one cache line.

          */

         cycle_t cycle_last ____cacheline_aligned_in_smp;

         u64 xtime_nsec;

...

};

仔细看看那个read回调函数的返回值,竟然是个cycle_t。内核的设计者简直就是神,cycle_t这个值没有和任何时间的概念关联,具体怎么关联就留给clock_event_device和具体的环境了,你可以通过两个cycle_t相减得到一个值,你也可以...那么现在看一个具体的:

static struct clocksource clocksource_pit = {

         .name   = "pit",

         .rating = 110,

         .read   = pit_read,

         .mask   = CLOCKSOURCE_MASK(32),

         .mult   = 0,

         .shift  = 20,

};

这回明白了吗?clocksource最重要的就是随时我调用你的read函数,你能返回我一个cycle_t,告诉我当前时钟源的一个计数器的值就行了,至于我要这个值有什么用,你就别管了。实际上内核可以通过这个计数器的值计算很多东西。
像tsc时钟源,你指望它产生中断吗?那是不可能的,但是它却真的是一个时钟源,pit也是一个时钟源,但是他却可以产生中断,hpet也是一个,它不但 可以产生中断,还可以代替tsc,这岂不乱套了吗?产生中断的时钟怎么能和仅仅是个计数器的时钟是并列关系,一起注册并竞争全局clocksource 呢?如果是一个tsc竞争取得胜利怎么办啊,谁来产生中断呢?
如果谁有以上问题,那就是没有理解clocksource和clock_event_device 产生的初衷,它们哥俩的出现仅仅是为了协作管理时钟,为了使内核可以提供更加灵活的策略同时为了更加方便的扩展各个不同平台的时钟框架。如果tsc注册并 取得全局clocksource权,那么也是没有关系的,产生中断的时钟不管你的软件怎么样,总会存在的,若不存在,你的计算机根本起不来,那就是出大问题了,仔细看看下面的代码:

void __init time_init_hook(void)

{

         irq0.mask = cpumask_of_cpu(0);

         setup_irq(0, &irq0);

}

在 一切就绪后(比如,tsc就是全局clocksource了),上述函数被调用,开始了心跳处理,也就是中断处理,而在这之前,时钟中断也是有的,只不过没有处理函数,进去中断马上就出来了,现在全局的clocksource有了,全局的clock_event_device也有了,于是它们便开始了各司 其职完成时钟处理。
  tsc仅仅是一个计数器,hpet可以产生中断同时有很多计数器,pit可以产生中断也有计数器,可以产生中断的产生中断,有计数器的开始计数,这些在你 按下计算机电源的那一瞬间就开始了,而不是等你在软件里面设置了一些东西才开始的,当然你完全可以用软件控制它们,一个很好的例子就是one-shot模 式,这个就不说了,看代码吧

原文链接: https://blog.csdn.net/dog250/article/details/5303056

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    关于linux内核中使用的时钟

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/410495

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月26日 下午12:03
下一篇 2023年4月26日 下午12:03

相关推荐