內核版本:Linux 2.6.18
平臺: FOR ARM
?
搞清RTC在kernel內的作用:
?
linux系統有兩個時鐘:一個是由主板電池驅動的“Real Time Clock”也叫做RTC或者叫CMOS時鐘,
硬件時鐘。當操作系統關機的時候,用這個來記錄時間,但是對于運行的系統是不用這個時間的。
另一個時間是 “System clock”也叫內核時鐘或者軟件時鐘,是由軟件根據時間中斷來進行計數的,
內核時鐘在系統關機的情況下是不存在的,所以,當操作系統啟動的時候,內核時鐘是要讀取RTC時間
來進行時間同步。并且在系統關機的時候將系統時間寫回RTC中進行同步。
硬件時鐘。當操作系統關機的時候,用這個來記錄時間,但是對于運行的系統是不用這個時間的。
另一個時間是 “System clock”也叫內核時鐘或者軟件時鐘,是由軟件根據時間中斷來進行計數的,
內核時鐘在系統關機的情況下是不存在的,所以,當操作系統啟動的時候,內核時鐘是要讀取RTC時間
來進行時間同步。并且在系統關機的時候將系統時間寫回RTC中進行同步。
?
如前所述,Linux內核與RTC進行互操作的時機只有兩個:
1) 內核在啟動時從RTC中讀取啟動時的時間與日期;
2) 內核在需要時將時間與日期回寫到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 current time of day (the wall time) is defined in kernel/timer.c:
struct timespec xtime;
The timespec data structure is defined in
struct timespec {
??????? time_t tv_sec;?????????????? /* seconds */
??????? long tv_nsec;??????????????? /* nanoseconds */
};
??????? 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();
{
?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
}
?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);
}
/* 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;
}
}
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 */
int set_rtc_mmss(unsigned long nowtime); /* write time into RTC chip */
?
不過我加了printk測試了一下,好象arch/cris/kernel/time.c這個文件和這兩個函數只是適用與X86?
ARM平臺啟動時并不走這邊.因此執行不到這些函數。
那ARM平臺啟動時,系統是在哪讀RTC的值并對內核時鐘(WallTime)進行初始化的呢?
ARM平臺啟動時并不走這邊.因此執行不到這些函數。
那ARM平臺啟動時,系統是在哪讀RTC的值并對內核時鐘(WallTime)進行初始化的呢?
?
已解決:
嵌入式Linux內核(ARM)是在系統啟動時執行/etc/init.d/hwclock.sh腳本,這個腳本會調用hwclock小程序讀取RTC的值并設置系統時鐘。
(換句話說,這要取決于你制作的文件系統里是否有這樣的腳本)
嵌入式Linux內核(ARM)是在系統啟動時執行/etc/init.d/hwclock.sh腳本,這個腳本會調用hwclock小程序讀取RTC的值并設置系統時鐘。
(換句話說,這要取決于你制作的文件系統里是否有這樣的腳本)
/* /etc/init.d/hwclock.sh */
DAEMON1=/sbin/hwclock
start() {
??? local RET ERROR=
start() {
??? local RET ERROR=
??? [ ! -f /etc/adjtime ] &&? echo "0.0 0 0.0" > /etc/adjtime
??? log_status_msg "Setting the System Clock using the Hardware Clock as reference..." -n
??? log_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
??? # 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
}
??? # 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);
}
??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在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;
?else
??next_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是這樣的:
/*
?* 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;
}
?* 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;
}
評論