时间管理在内核中占有非常重要的地位。相对于时间驱动,内核中有大量的函数都是基于时间驱动的。内核必须管理系统的运行时间以及当前的日期和时间。
首先搞清楚 RTC 在 Kernel 内的作用:
Linux 系统有两个时钟:实时时钟和系统定时器
一、实时时钟
一个是由纽扣电池供电的 “Real Time Clock” 也叫作 RTC(实时时钟)或者叫 CMOS 时钟,硬件时钟。当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。当系统启动时,内核通过读取 RTC 来初始化墙上时间,改时间存放在 xtime 变量中。所谓墙上时间也就是当前的实际时间。
二、系统定时器
另一个时间是 “System Clock” 也叫作内核时钟或者软件时钟或者叫系统定时器,是由软件根据时间中断来进行计数的,系统定时器是内核时间机制中最重要的一部分,它提供了一种周期性触发中断机制,即系统定时器以 HZ(时钟节拍率)为频率自行触发时钟中断。当时钟中断发生时,内核就通过时钟中断处理程序 timer_interrupt() 对其进行处理。
系统定时器完全由操作系统管理,因此也称为系统时钟或者软件时钟。当系统启动时,内核通过 RTC 初始化系统定时器,系统定时器接着由操作系统长官,进行固定频率的定时。
可以看到,系统时间并不是传统意义上的那种计时时钟,而是通过定时这种特殊的方式来表现时间。内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时钟是尧都区 RTC 时间来进行时间同步。并且在系统关机的时候将系统时间写回 RTC 中进行同步。
2.1、jiffies
全局变量 jiffies 用来记录自系统启动依赖产生的节拍的总数。它被用来记录系统自开机以来,已经过了多少 tick。每发生一次 timer interrupt,jiffies 变量会被加一。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增加该变量的值。因为一秒内时钟中断的次数等于 HZ,所以 jiffies 一秒内增加的值也就为 HZ,系统运行时间以秒为单位计算,就等于 jiffies/HZ。
- jiffies 转换为秒可采用公式:(jiffies/HZ)计算;
- 将秒转换为 jiffies 可采用公式:(jiffies/HZ)计算;
- Tick 是 HZ 的倒数,意即 timer interrupt 每发生一次中断的时间;
- jiffies 仅是相对于系统启动的相对时间,如果想获取 absolute time 或者 wall time,则需要使用 RTC,内核用变量 xtime 来记录,当系统启动时,读取 RTC 并记录在 xtime 中,当系统 halt 时,则将 wall time 写回 RTC,函数 do_gettimeofday() 来读取 wall time。
系统定时器及其中断处理程序是内核管理机制的中枢,下面是一些利用系统定时器周期执行的工作(中断处理程序所做的工作):
- 内核在启动时从 RTC 中读取启动时的时间与日期(Linux 系统时间的初始化);通过调用 rtc_read_time(rtc, &tm) 读出 RTC 时间;调用 do_settimeofday(&tv) 给系统时间 xtime 初始化。
- 内核在需要时将时间与日期写回到 RTC 中。系统启动时,内核通过读取 RTC 来初始化内核时钟,又叫墙上时间,改时间放在 xtime 变量中。
系统睡眠的时候 CPU 要断电系统时钟不工作,所以 RTC 的睡眠函数 rtc_suspend 读出 RTC 和系统时钟的时间,计算它们之间的差,然后保存到静态变量。系统被唤醒后读出当前 RTC 的时间和系统的时间,系统时间应该接近 0,因为 CPU 刚刚恢复上电,将原来保存的 RTC 和系统之间的时间差加上刚刚读到的 RTC 的时间就是最新的系统时间,将计算出来的时间通过 rtc_resume 函数调用 timekeeping_inject_sleeptime(&sleep_time) 重新初始化系统时钟 xtime。如果 RTC 的时间和系统时间都一样,那么它们之间的差为 0。
但是有些系统硬件 RTC 时间是不可以写,只能够被读,那么他们之间的差就不为 0 了。