Linux系统时间与RTC时间【转】
生活随笔
收集整理的這篇文章主要介紹了
Linux系统时间与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 <linux/rtc.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <time.h> #include <err.h>int main(void) {unsigned long i = 0;unsigned long data = 0;int fd = 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 = 0; i < 100; i++ ){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 <stdio.h> #include <stdlib.h> #include <linux/rtc.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <errno.h>int main(void) {int i, fd, retval, irqcount = 0;unsigned long tmp, data;struct rtc_time rtc_tm;fd = open ("/dev/rtc", O_RDONLY);if (fd == -1) {perror("/dev/rtc");exit(1);}// Alarm example,10 mintues later alarm/* Read the RTC time/date */retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);if (retval == -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 + 1, rtc_tm.tm_year + 1900,rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);// Setting alarm timertc_tm.tm_min += 10;if (rtc_tm.tm_sec >= 60) {rtc_tm.tm_sec %= 60;rtc_tm.tm_min++;}if (rtc_tm.tm_min == 60) {rtc_tm.tm_min = 0;rtc_tm.tm_hour++;}if (rtc_tm.tm_hour == 24)rtc_tm.tm_hour = 0;// settingretval = ioctl(fd, RTC_ALM_SET, &rtc_tm);if (retval == -1) {perror("ioctl");exit(1);}/* Read the current alarm settings */retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);if (retval == -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 = ioctl(fd, RTC_AIE_ON, 0);if (retval == -1) {perror("ioctl");exit(1);}/* This blocks until the alarm ring causes an interrupt */retval = read(fd, &data, sizeof(unsigned long));if (retval == -1) {perror("read");exit(1);}irqcount++;fprintf(stderr, " okay. Alarm rang.\n"); }------------------------------------------------------------------------------------------------------------------------------------------------S3C2410 RTC(Real Time Clock)簡介 實時時鐘(RTC)單元可以在系統電源關半閉的情況下依靠備用電池工作。RTC可以通過使用STRB/LDDRB這兩個ARM指令向CPU傳遞8位數據(BCD碼)。數據包括秒、分、小時、日期、天、月、和年。RTC單元依靠一個外部的32.768kHZ的石晶,也可以執行報警功能。特性BCD碼:秒、分、時、日期、天、月和年潤年產生器報警功能:報警中斷,或者從power-off狀態喚醒。移除了2000年的問題獨立的電源引角:RTCVDD為RTOS內核時間Tick time支持毫秒Tick time中斷。Round reset 功能。RTC在power-off模式或者正常操作模式時可以在一指定的時間產生一個報警信號。在正常操作模式下,報警中斷(ALMINT)被激活,在power-off模式下,電源管理喚醒信號(PMWKUP)和ALMINT一起被激活。RTC報警寄存器(RTCALM)決定報警的enable/disable狀態和報警時間設定的條件。RTC TICK TIME被用于中斷請求。TICNT寄存器有一個中斷使能位和中斷的計數值。當計數值到達0時TICK TIME中斷。所以中斷的周期如下:周期= (n+1 ) /128 秒n:Tick time計數值(1~127)這個RTC time tick可以被用于實時操作系統(RTOS)內核 time tick。如果time tick通過RTC time tick產生,那么RTOS的時間相關的功能就需要總是與實時時間同步。ROUND RESET 功能Rund reset功能可以通過RTC round reset寄存器(RTCRST)來執行。 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) += rtc-lib.oobj-$(CONFIG_RTC_HCTOSYS) += hctosys.oobj-$(CONFIG_RTC_CLASS) += rtc-core.ortc-core-y := class.o interface.ortc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.ortc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.ortc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.oobj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o其中 rtc-lib.c :提供了一些時間格式相互轉化的函數。 hctosys.c:在啟動時初始化系統時間。 RTC核心文件: class.c interface.c rtc-dev.c:字符設備的注冊和用戶層文件操作函數接口。 rtc-proc.c rtc-sysfs.c rtc-s3c.o:S3C2410 RTC的芯片平臺驅動。//// 4> 在根文件系統的 做的動作, 把 pc linux上的 /etc/localtime 復制到 板子的 /etc/下面即可5> mknod /dev/rtc c 254 0下面的動作只需做一次 ,一旦寫入RTC chip后, chip就自己計時了,除非電池沒電了。板子第一次啟動后,假如設置系統時間為2007年10月2日,13:49分,可以這樣設置1> date 1002134920072> hwclock –w如果沒有出錯, 就已經把2007年10月2日,13:49分 寫入RTC chip了,測試:反復執行hwclock ,看看是否時間在變化。3> 重啟板子, 測試, 執行hwclock ,看看時間是否在流逝 。?
轉載于:https://www.cnblogs.com/sky-heaven/p/5220873.html
總結
以上是生活随笔為你收集整理的Linux系统时间与RTC时间【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Https环境下WS接口两次连续调用出错
- 下一篇: 【Bugly 技术干货】Android开