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
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。这个可用来衡量虚机性能。