时间篇之linux系统时间和RTC时间
一、linux系统下包含两个时间:系统时间(刚启动时读取的是rtc时间)和RTC时间。
一般情况下都会选择芯片上最高精度的定时器作为系统时间的定时基准,
以避免在系统运行较长时间后出现大的时间偏移。特点是掉电后不保存。
所以一旦你重启机器后,那么系统需要重新从RTC上重新获取时间,保存到系统内核文件中。
RTC(real_time clock)驱动程序,可以在E:\linux内核\linux-2.6.0\linux-2.6.0\drivers\char\rtc.c中找到。
设备接口就是 /dev/rtc, 他负责跟rtc打交道,并读取rtc中维护的时间.
它是一个从系统定时器中独立出来的虚拟设备,用于设置系统时钟,提供报警器或周期性的定时器. 那么系统时间一直运行吗? 显然在操作系统关闭或重启期间,服务器宕机期间,整个服务器的时间就依赖于RTC芯片。 从这我们看出linux系统时间和RTC时间是两套独立的计时体系,但它们之间又是相互依存的: 1)刚安装操作系统后,若在安装过程不设置系统时间,那么默认的系统时间就是从服务器的RTC芯片中获取当前的硬件时间; 2)在linux操作系统中,一旦修改系统时间后,又重启或关闭Linux系统,则OS通常会将系统时间更新到RTC; 3)在操作系统再次启动的时候,Linux OS则会再次从RTC中获取当前的时间。 服务器异常下电后,待操作系统重新启动后,发现系统时间发生了跳变? 其原因通常是:修改了操作系统时间,在服务器异常下电后,操作系统并未及时将修改后的时间更新到RTC,导致操作系统重新启动后, 就会从RTC芯片中加载了之前“老”的时间,从而在操作系统层面体现为“时间跳变”
二、关于jiffies
一次中断时间间隔叫一个tick,即每个触发周期的时间叫做tick, 一秒内时钟中断的次数(每个时间间隔就是一次)等于Hz hz别名就是tick rate(HZ) linux系统查看hz: [root@k3master ~]# cat /boot/config-`uname -r` | grep 'CONFIG_HZ=' CONFIG_HZ=1000 1hz就是每秒1000次中断 每次时钟中断处理程序即每发生一次tick都会增加jiffies该变量的值, jiffies一秒内增加的值也就是Hz(频率),比如:linux下默认是 1000次时钟中断次数/秒 系统运行时间以秒为单位,换算方法等于jiffies/Hz。 jiffies是linux内核中的一个全局变量,记录内核节拍时间的数值,内核在开机启动的时候会读取RTC获取一个时间作为基准值, 这个基准时间对应一个jiffies,RTC时间,只在开机时候读取一次,时钟节拍的时间取决于操作系统的配置, [root@k3master ~]# cat /boot/config-`uname -r` | grep 'CONFIG_HZ=' CONFIG_HZ=1000 内核中记录用HZ来记录和表示,1000hz对应就是1/hz,也就是1ms。 jiffies是内核中的一个全局变量,用来记录自系统启动一来产生的节拍数。譬如,如果计算系统运行了多长时间,可以用 jiffies/tick rate 来计算。
三、jiffies内核源码
E:\linux内核\linux-2.6.0\linux-2.6.0\include\linux\jiffies.h
#ifndef _LINUX_JIFFIES_H
#define _LINUX_JIFFIES_H
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/seqlock.h>
#include <asm/system.h>
#include <asm/param.h> /* for HZ */
/*
* The 64-bit value is not volatile - you MUST NOT read it
* without holding read_lock_irq(&xtime_lock).
* get_jiffies_64() will do this for you as appropriate.
*/
extern u64 jiffies_64;
extern unsigned long volatile jiffies;
#if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void);
#else
static inline u64 get_jiffies_64(void)
{
return (u64)jiffies;
}
#endif
/*
* These inlines deal with timer wrapping correctly. You are
* strongly encouraged to use them
* 1. Because people otherwise forget
* 2. Because if the timer wrap changes in future you won't have to
* alter your driver code.
*
* time_after(a,b) returns true if the time a is after time b.
*
* Do this with "<0" and ">=0" to only test the sign of the result. A
* good compiler would generate better code (and a really good compiler
* wouldn't care). Gcc is currently neither.
*/
#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)
#endif
注意:date -s 2007-08-03 命令设置时间只会影响系统时间,不会设置RTC时间,
如果需要把当前系统时间同步设置到RTC中,需要额外调用hwclock命令。
四、我们在看看date相关源码
E:\linux内核\linux-2.6.0\linux-2.6.0\drivers\char\rtc.c E:\linux内核\linux-2.6.0\linux-2.6.0\kernel\time.c
//开放给用户空间的
E:\linux内核\linux-2.6.0\linux-2.6.0\arch\x86_64\kernel\time.c
1、我们在看看rtc时钟驱动
linux-2.6.0\linux-2.6.0\drivers\char\rtc.c //的部分内容
* This driver allows use of the real time clock (built into * nearly all computers) from user space. It exports the /dev/rtc * interface supporting various ioctl() and also the * /proc/driver/rtc pseudo-file for status information. * The /dev/rtc interface will block on reads until an interrupt * has been received. If a RTC interrupt has already happened, * it will output an unsigned long and then block. The output value * contains the interrupt status in the low byte and the number of * interrupts since the last read in the remaining high bytes. The * /dev/rtc interface can also be used with the select(2) call.
2、RTC时间:
是指系统中包含的RTC芯片内部所维护的时间。RTC芯片都有电池+系统电源的双重供电机制,
在系统正常工作时由系统供电,在系统掉电后由电池进行供电。因此系统电源掉电后RTC时间仍然能够正常运行。
从工作原理来说,RTC实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源,对该时钟源脉冲进行计数。主要靠晶振来保障准确性。
每次Linux系统启动后在启动过程中会检测和挂载RTC驱动,在挂载后会自动从RTC芯片中读取时间并设置到系统时间中去。
此后如果没有显式的通过命令去控制RTC的读写操作,系统将不会再从RTC中去获取或者同步设置时间。
hwclock 命令使用
//读取RTC时间(cmos时间)并设置到系统时间中去,hwclock -s 读取RTC时间(cmos时间)并设置到系统时间中去,我们看得很清楚了,时间上和我之前的系统时间不一样了
[root@k3master ~]# hwclock -r Mon 07 Mar 2022 10:48:18 PM CST -0.598781 seconds [root@k3master ~]# hwclock -s [root@k3master ~]# date Mon Mar 7 22:48:55 CST 2022 //把当前的系统时间设置到RTC中 [root@k3master ~]# date Mon Mar 7 22:52:20 CST 2022 [root@k3master ~]# hwclock -w [root@k3master ~]# hwclock -r Mon 07 Mar 2022 10:52:45 PM CST -0.614313 seconds
//如果你想启动时自动执行RTC时间同步到系统时间,可以把
hwclock -s加入到rc.local文件中。
3、RTC芯片举例:
4、看一下RTC芯片工作的驱动模型
E:\linux内核\linux-2.6.0\linux-2.6.0\kernel\time.c