热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux系统时间与RTC时间

http:bbs.chinaunix.netforum.php?modviewthread&tid3637782Linux的RTC驱动相对还是比较简单的,可以将它作为

http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3637782

 



Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符型设备,或者一个misc设备,也可以是一个平台设备,这都没有关系,主要还是对rtc_ops这个文件操作结构体中的成员填充,这里主要涉及到两个方面比较重要:1. 在Linux中有硬件时钟与系统时钟等两种时钟。硬件时钟是指主机板上的时钟设备,也就是通常可在BIOS画面设定的时钟。系统时钟则是指kernel中的时钟。当Linux启动时,系统时钟会去读取硬件时钟的设定,之后系统时钟即独立运作。所有Linux相关指令与函数都是读取系统时钟的设定。系统时钟的设定就是我们常用的date命令,而我们写的RTC驱动就是为硬件时钟服务的,它有属于自己的命令hwclock,因此使用date命令是不可能调用到我们的驱动的(在这点上开始把我郁闷到了,写完驱动之后,傻傻的用date指令来测试,当然结果是什么都没有),我们可以通过hwclock的一些指令来实现更新rtc时钟——也就是系统时钟和硬件时钟的交互。hwclock –r 显示硬件时钟与日期hwclock –s 将系统时钟调整为与目前的硬件时钟一致。hwclock –w 将硬件时钟调整为与目前的系统时钟一致。2. 第二点就是内核空间和用户空间的交互,在系统启动结束,我们实际是处在用户态,因此我们使用指令输入的内容也是在用户态,而我们的驱动是在内核态的,内核态和用户态是互相不可见的,因此我们需要特殊的函数来实现这两种形态的交互,这就是以下两个函数:copy_from_user(从用户态到内核态)copy_to_user (从内核态到用户态)当然这两个函数需要我们在内核驱动中实现。RTC最基本的两个命令就是设置时间,读取时间。设置时间——设置时间会调用系统默认的RTC_SET_TIME,很显然就是处在用户态的用户将自己所要设置的时间信息传递给内核态,case RTC_SET_TIME:{struct rtc_time rtc_tm;if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))return -EFAULT;sep4020_rtc_settime(&rtc_tm);//把用户态得到的信息传递给设置时间这个函数return 0;}读取时间——设置时间会调用系统默认的RTC_RD_TIME,很显然就是需要通过内核态的驱动将芯片时钟取出,并传递给用户态case RTC_RD_TIME: /* Read the time/date from RTC */{sep4020_rtc_gettime(&septime);//通过驱动的读函数读取芯片时钟
copy_to_user((void *)arg, &septime, sizeof septime);//传递给用户态
}--------------------------------------------------------------------------------------------------------------------首先搞清楚RTC在kernel内的作用:linux系统有两个时钟:一个是由主板电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,
硬件时钟。当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。
另一个时间是 “System clock”也叫内核时钟或者软件时钟,是由软件根据时间中断来进行计数的,
内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时钟是要读取RTC时间
来进行时间同步。并且在系统关机的时候将系统时间写回RTC中进行同步。如前所述,Linux内核与RTC进行互操作的时机只有两个:
1) 内核在启动时从RTC中读取启动时的时间与日期;
2) 内核在需要时将时间与日期回写到RTC中。系统启动时,内核通过读取RTC来初始化内核时钟,又叫墙上时间,该时间放在xtime变量中。
The current time of day (the wall time)
is defined in kernel/timer.c:
struct timespec xtime;
The timespec data structure
is defined in as:
struct timespec {time_t tv_sec; /* seconds */long tv_nsec; /* nanoseconds */
};
问题1:系统启动时在哪读取RTC的值并设置内核时钟进行时间同步的呢
?
最有可能读取RTC设置内核时钟的位置应该在arch
/arm/kernel/time.c里的time_init函数内.
time.c为系统的时钟驱动部分.time_init函数会在系统初始化时,由init
/main.c里的start_kernel函数内调用.X86架构就是在这里读RTC值并初始化系统时钟xtime的. ARM架构的time_init代码如下:
/* arch/arm/kernel/time.c */
void __init time_init(void)
{
if (system_timer->offset == NULL)system_timer->offset = dummy_gettimeoffset;
system_timer
->init();
#ifdef CONFIG_NO_IDLE_HZ
if (system_timer->dyn_tick)system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;
#endif
}上 面system_timer
->init()实际执行的是时钟驱动体系架构相关(具体平台)部分定义的init函数,若是s3c2410平台,则执 行的为arch/arm/mach-s3c2410/time.c里定义的s3c2410_timer_init函数.不过 s3c2410_timer_init()也没有读RTC的代码.整个时钟驱动初始化的过程大致就执行这些代码.
既然在系统时钟驱动初始化的过程中没有读RTC值并设置内核时钟,那会在哪设置呢
?我搜了一下,发现内核好象只有在arch/cris/kernel/time.c里有RTC相关代码,如下:
/* arch/cris/kernel/time.c */
/* grab the time from the RTC chip */
//读RTC的函数
unsigned long get_cmos_time(void)
{
unsigned
int year, mon, day, hour, min, sec;
sec
= CMOS_READ(RTC_SECONDS);
min
= CMOS_READ(RTC_MINUTES);
hour
= CMOS_READ(RTC_HOURS);
day
= CMOS_READ(RTC_DAY_OF_MONTH);
mon
= CMOS_READ(RTC_MONTH);
…………
return mktime(year, mon, day, hour, min, sec);
}这个函数会在update_xtime_from_cmos内被调用:
void update_xtime_from_cmos(void)
{
if(have_rtc) {xtime.tv_sec = get_cmos_time();xtime.tv_nsec = 0;
}
}另外还有设置rtc的函数
int set_rtc_mmss(unsigned long nowtime); /* write time into RTC chip */不过我加了printk测试了一下,好象arch/cris/kernel/time.c这个文件和这两个函数只是适用与X86?
ARM平台启动时并不走这边.因此执行不到这些函数。
那ARM平台启动时,系统是在哪读RTC的值并对内核时钟(WallTime)进行初始化的呢?已解决:
嵌入式Linux内核(ARM)是在系统启动时执行
/etc/init.d/hwclock.sh脚本,这个脚本会调用hwclock小程序读取RTC的值并设置系统时钟。
(换句话说,这要取决于你制作的文件系统里是否有这样的脚本)
/* /etc/init.d/hwclock.sh */
DAEMON1
=/sbin/hwclock
start() {local RET ERROR
=[ ! -f /etc/adjtime ] && echo "0.0 0 0.0" > /etc/adjtimelog_status_msg "Setting the System Clock using the Hardware Clock as reference..." -n# Copies Hardware Clock time to System Clock using the correct# timezone for hardware clocks in local time, and sets kernel# timezone. DO NOT REMOVE.[ "$HWCLOCKACCESS" != no ] && $DAEMON1 --hctosys $GMT $BADYEAR## Now that /usr/share/zoneinfo should be available,# announce the local time.#log_status_msg "System Clock set. Local time: `date`"log_status_msg ""return 0
}
hwclock最先读取的设备文件是
/dev/rtc ,busybox里面的hwclock是这样实现的:
static int xopen_rtc(int flags)
{
int rtc;
if (!rtcname) {rtc = open("/dev/rtc", flags);if (rtc >= 0)return rtc;rtc = open("/dev/rtc0", flags);if (rtc >= 0)return rtc;rtcname = "/dev/misc/rtc";
}
return xopen(rtcname, flags);
}
2. 内核如何更新RTC时钟?
通过set_rtc函数指针指向的函数,set_rtc在arch
/arm/kernel/time.c内
/* arch/arm/kernel/time.c */
/*
* hook for setting the RTC's idea of the current time.
*/
int (*set_rtc)(void);
但是set_rtc函数指针在哪初始化的呢?set_rtc应该是和RTC驱动相关的函数.
搜索kernel源码后发现,好象内核其他地方并没有对其初始化。待解决!
set_rtc在do_set_rtc内调用
static inline void do_set_rtc(void)
{
……
if (set_rtc())/** rtc update failed. Try again in 60s*/next_rtc_update = xtime.tv_sec + 60;
elsenext_rtc_update = xtime.tv_sec + 660; /* update every ~11 minutes by default*/
}do_set_rtc在timer_tick里调用
/*
* Kernel system timer support.
*/
void timer_tick(struct pt_regs *regs)
{
profile_tick(CPU_PROFILING, regs);
do_leds();
do_set_rtc();
do_timer(
1);
……
}
timer_tick为Kernel提供的体系架构无关的时钟中断处理函数,通常会在体系架构相关的时钟中断处理函数内调用它。如s3c2410是这样的:
在arch
/arm/mach-s3c2410/time.c中
* IRQ handler for the timer
*/
static irqreturn_t
s3c2410_timer_interrupt(
int irq, void *dev_id, struct pt_regs *regs)
{
write_seqlock(
&xtime_lock);
timer_tick(regs);
write_sequnlock(
&xtime_lock);
return IRQ_HANDLED;
}
*nix 下 timer机制 标准实现,一般是用 sigalarm + setitimer() 来实现的,但这样就与 select/epoll 等逻辑有所冲突,我希望所有 event 的通知逻辑都从 select/epoll 中触发。(FreeBSD 中 kqueue 默认就有 FILTER_TIMER,多好)ps. /dev/rtc 只能被 open() 一次,因此上面希望与 epoll 合并的想法基本不可能了~下面是通过 /dev/rtc (real-time clock) 硬件时钟实现的 timer机制。:-)
其中 ioctl(fd, RTC_IRQP_SET,
4) 的第三个参数只能是 2, 4, 8, 16, 32 之一,表示 xx Hz。-------------------------------------------------
#include

#include

#include

#include

#include

#include

#include

#include

#include

#include
int main(void)
{unsigned
long i &#61; 0;unsigned long data &#61; 0;int fd &#61; open("/dev/rtc", O_RDONLY);if ( fd <0 )errx(1, "open() fail");/* set the freq as 4Hz */if ( ioctl(fd, RTC_IRQP_SET, 4) <0 )errx(1, "ioctl(RTC_IRQP_SET) fail");/* enable periodic interrupts */if ( ioctl(fd, RTC_PIE_ON, 0) <0 )errx(1, "ioctl(RTC_PIE_ON)");for ( i &#61; 0; i <100; i&#43;&#43; ){if ( read(fd, &data, sizeof(data)) <0 )errx(1, "read() error");printf("timer %d\n", time(NULL));}/* enable periodic interrupts */if ( ioctl(fd, RTC_PIE_OFF, 0) <0 )errx(1, "ioctl(RTC_PIE_OFF)");close(fd);return 0;
}
--------------------------------------------------------------------------------------------------------------------User mode test code:#include
#include

#include

#include

#include

#include

#include

#include

#include
int main(void)
{
int i, fd, retval, irqcount &#61; 0;unsigned long tmp, data;struct rtc_time rtc_tm;fd &#61; open ("/dev/rtc", O_RDONLY);if (fd &#61;&#61; -1) {perror("/dev/rtc");exit(1);}// Alarm example&#xff0c;10 mintues later alarm/* Read the RTC time/date */retval &#61; ioctl(fd, RTC_RD_TIME, &rtc_tm);if (retval &#61;&#61; -1) {perror("ioctl");exit(1);}fprintf(stderr, "Current RTC date/time is %d-%d-%d,%02d:%02d:%02d.\n", rtc_tm.tm_mday, rtc_tm.tm_mon &#43; 1, rtc_tm.tm_year &#43; 1900,rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);// Setting alarm timertc_tm.tm_min &#43;&#61; 10;if (rtc_tm.tm_sec >&#61; 60) {rtc_tm.tm_sec %&#61; 60;rtc_tm.tm_min&#43;&#43;;}if (rtc_tm.tm_min &#61;&#61; 60) {rtc_tm.tm_min &#61; 0;rtc_tm.tm_hour&#43;&#43;;}if (rtc_tm.tm_hour &#61;&#61; 24)rtc_tm.tm_hour &#61; 0;// settingretval &#61; ioctl(fd, RTC_ALM_SET, &rtc_tm);if (retval &#61;&#61; -1) {perror("ioctl");exit(1);}/* Read the current alarm settings */retval &#61; ioctl(fd, RTC_ALM_READ, &rtc_tm);if (retval &#61;&#61; -1) {perror("ioctl");exit(1);}fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);/* Enable alarm interrupts after setting*/retval &#61; ioctl(fd, RTC_AIE_ON, 0);if (retval &#61;&#61; -1) {perror("ioctl");exit(1);}/* This blocks until the alarm ring causes an interrupt */retval &#61; read(fd, &data, sizeof(unsigned long));if (retval &#61;&#61; -1) {perror("read");exit(1);}irqcount&#43;&#43;;fprintf(stderr, " okay. Alarm rang.\n");
}
------------------------------------------------------------------------------------------------------------------------------------------------S3C2410 RTC&#xff08;Real Time Clock)简介
实时时钟(RTC)单元可以在系统电源关半闭的情况下依靠备用电池工作。RTC可以通过使用STRB&#xff0f;LDDRB这两个ARM指令向CPU传递8位数据&#xff08;BCD码&#xff09;。数据包括秒、分、小时、日期、天、月、和年。RTC单元依靠一个外部的32.768kHZ的石晶&#xff0c;也可以执行报警功能。特性BCD码&#xff1a;秒、分、时、日期、天、月和年润年产生器报警功能&#xff1a;报警中断&#xff0c;或者从power
-off状态唤醒。移除了2000年的问题独立的电源引角&#xff1a;RTCVDD为RTOS内核时间Tick time支持毫秒Tick time中断。Round reset 功能。RTC在power-off模式或者正常操作模式时可以在一指定的时间产生一个报警信号。在正常操作模式下&#xff0c;报警中断&#xff08;ALMINT&#xff09;被激活&#xff0c;在power-off模式下&#xff0c;电源管理唤醒信号&#xff08;PMWKUP&#xff09;和ALMINT一起被激活。RTC报警寄存器&#xff08;RTCALM&#xff09;决定报警的enable/disable状态和报警时间设定的条件。RTC TICK TIME被用于中断请求。TICNT寄存器有一个中断使能位和中断的计数值。当计数值到达0时TICK TIME中断。所以中断的周期如下&#xff1a;周期&#xff1d; &#xff08;n&#43;1 ) /128 秒n&#xff1a;Tick time计数值&#xff08;1&#xff5e;127&#xff09;这个RTC time tick可以被用于实时操作系统&#xff08;RTOS&#xff09;内核 time tick。如果time tick通过RTC time tick产生&#xff0c;那么RTOS的时间相关的功能就需要总是与实时时间同步。ROUND RESET 功能Rund reset功能可以通过RTC round reset寄存器&#xff08;RTCRST&#xff09;来执行。 The round boundary (30, 40, or 50 sec.) of the second carry generation can be selected, and the second value is rounded to zero in the round reset. For example, when the current time is 23:37:47 and the round boundary is selected to 40 sec, the round reset changes the current time to 23:38:00.NOTEAll RTC registers have to be accessed for each byte unit using the STRB and LDRB instructions or char type pointer.---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------在.../drivers/rtc/Makefile中与我们有关的项有obj-$(CONFIG_RTC_LIB) &#43;&#61; rtc-lib.oobj-$(CONFIG_RTC_HCTOSYS) &#43;&#61; hctosys.oobj-$(CONFIG_RTC_CLASS) &#43;&#61; rtc-core.ortc-core-y :&#61; class.o interface.ortc-core-$(CONFIG_RTC_INTF_DEV) &#43;&#61; rtc-dev.ortc-core-$(CONFIG_RTC_INTF_PROC) &#43;&#61; rtc-proc.ortc-core-$(CONFIG_RTC_INTF_SYSFS) &#43;&#61; rtc-sysfs.oobj-$(CONFIG_RTC_DRV_S3C) &#43;&#61; rtc-s3c.o其中 rtc-lib.c &#xff1a;提供了一些时间格式相互转化的函数。 hctosys.c&#xff1a;在启动时初始化系统时间。 RTC核心文件&#xff1a; class.c interface.c rtc-dev.c&#xff1a;字符设备的注册和用户层文件操作函数接口。 rtc-proc.c rtc-sysfs.c rtc-s3c.o&#xff1a;S3C2410 RTC的芯片平台驱动。////
4> 在根文件系统的 做的动作&#xff0c; 把 pc linux上的 /etc/localtime 复制到 板子的 /etc/下面即可5> mknod /dev/rtc c 254 0下面的动作只需做一次 &#xff0c;一旦写入RTC chip后&#xff0c; chip就自己计时了&#xff0c;除非电池没电了。板子第一次启动后&#xff0c;假如设置系统时间为2007年10月2日&#xff0c;13&#xff1a;49分&#xff0c;可以这样设置1> date 1002134920072> hwclock –w如果没有出错&#xff0c; 就已经把2007年10月2日&#xff0c;13&#xff1a;49分 写入RTC chip了&#xff0c;测试&#xff1a;反复执行hwclock &#xff0c;看看是否时间在变化。3> 重启板子&#xff0c; 测试&#xff0c; 执行hwclock &#xff0c;看看时间是否在流逝 。

推荐阅读
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 众筹商城与传统商城的区别及php众筹网站的程序源码
    本文介绍了众筹商城与传统商城的区别,包括所售产品和玩法不同以及运营方式不同。同时还提到了php众筹网站的程序源码和方维众筹的安装和环境问题。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
author-avatar
范大少微博劳
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有