linux时钟有很多种,粗略来分可分成两类:提供中断的周期性时钟(rtc、pit等),提供计数的递增型时钟(如tsc)

这里简单罗列几种常见的。

(1)RTC

(2)TSC

(3)KVM_CLOCK

(4)acpi_pm

查看当前系统支持的时钟

cat /sys/devices/system/clocksource/clocksource0/available_clocksource

查看当前使用的时钟

cat /sys/devices/system/clocksource/clocksource0/current_clocksource

RTC


linux系统计时器_第1张图片

RTC系统结构图

RTC通过独立电池供电,系统可从RTC读取时间信息,来确保断电后时间运行连续性。在内核中RTC驱动可分为两层,一层为抽象层(与硬件无关)用于管理RTC设备、设备节点、属性节点注册及操作。另一层为底层驱动层(硬件相关)

抽象层程序在/drivers/rtc目录下,主要涉及下面几个文件:

  • class.c   用于管理和注册RTC设备结构、sysfs、procfs及RTC类;

  • rtc-dev.c 用于注册和管理RTC设备节点,为用户空间提供devfs操作接口,主要操作有rtc_read,rtc_ioctl; 

  • rtc-proc.c  用于管理rtc的procfs属性节点,提供一些中断状态、标志查询;

  • rtc-sysfs.c 用于管理rtc设备的sysfs属性,如获取RTC设备名字、日期、时间等属性信息;

  • interface.c 为rtc-dev.c 和RTC低层驱动提供操作接口

RTC设备驱动通过rtc_device_register向系统注册rtc设备,并在proc、sys等目录下生成相应属性文件。当用户通过/dev/rtcx设备节点发起操作的时候,都需要通过interface接口才能访问到真实的设备驱动。

在低频业务场景下,用户进程通过read(2)、select(2)读取/dev/rtc来获取这些中断。当中断调用时,进程会阻塞或者退出直到下一个中断到来。在高频业务场景下,用户进程会检查一个中断数来判断当前是否有未处理的中断。

中断频率可以通过/proc/sys/dev/rtc/max-user-freq文件来修改(默认是64hz)

RTC底层操作接口

这些操作接口都封装在

int (*open)(struct device *);                           打开设备

void (*release)(struct device *);                    释放设备

int (*ioctl)(struct device *, unsigned int, unsigned long);    

int (*read_time)(struct device *, struct rtc_time *);   读取RTC时间;

int (*set_time)(struct device *, struct rtc_time *);    设置RTC时间;

int (*read_alarm)(struct device *, struct rtc_wkalrm *);  读取RTC报警时间;

int (*set_alarm)(struct device *, struct rtc_wkalrm *);   设置RTC报警时间;

int (*proc)(struct device *, struct seq_file *);                    用于提供procfs查询rtc状态接口;

int (*set_mmss)(struct device *, unsigned long secs);   设置以S为单位RTC时间接口;

int (*read_callback)(struct device *, int data);                        

int (*alarm_irq_enable)(struct device *, unsigned int enabled);  中断使能接口;


可以通过hwclock来同步系统时钟和硬件时钟信息,或者可通过0x70、0x71端口访问RTC

系统时间同步到硬件时间:hwclock –w、hwclock --systohc

在用hwclock进行显示,设置RTC硬件时间之前都会先通过进行时钟tick同步,同步方法是开启1S 一次update interrupt,每过1S RTC都会产生一次报警中断,并更新相应的中断数据,在应用层通过select来监控是否有RTC数据可读,如果有数据则会继续显示或设置硬件时间操作


TSC


TSC是位于CPU里面的一个64位的TSC寄存器,它记录自启动以来处理器消耗的时钟周期数,每个CPU时钟周期其值加一。TSC精度很高,因为TSC随着处理器周期速率的变化而变化。

可以通过rdtsc指令来读取。

[秒 = TSC值/CPU时钟速率]

  • 通过rdtsc指令来读取TSC值

taticinline unsigned longlong native_read_tsc(void){

     unsigned long longval;

     asm volatile("rdtsc": "=A" (val));

     return val;

}

这个函数将TSC值放在eax和edx寄存器中,然后再存往val变量中。

KVM_CLOCK


kvm_clock是kvm半虚拟化默认时钟源。大概原理是在客户机上实现一个kvmclock驱动,然后客户机通过这个驱动向VMM查询时间。

工作流程:客户机先分配一个内存页,这个内存页通过MSR寄存器告知给VMM,VMM将母机系统时间写入这个内存页,然后客户机通过读取这个内存页来获取时间。

cpu steal time 指vcpu等待cpu的时间,vcpu通过vm-exit进入vmm,再从vmm进入guest,这个过程就是cpu steal time。这个可用来衡量虚机性能。