內(nèi)核版本:Linux 2.6.18
平臺: FOR ARM
?
搞清RTC在kernel內(nèi)的作用:
?
linux系統(tǒng)有兩個時鐘:一個是由主板電池驅(qū)動的“Real Time Clock”也叫做RTC或者叫CMOS時鐘,
硬件時鐘。當操作系統(tǒng)關機的時候,用這個來記錄時間,但是對于運行的系統(tǒng)是不用這個時間的。
另一個時間是 “System clock”也叫內(nèi)核時鐘或者軟件時鐘,是由軟件根據(jù)時間中斷來進行計數(shù)的,
內(nèi)核時鐘在系統(tǒng)關機的情況下是不存在的,所以,當操作系統(tǒng)啟動的時候,內(nèi)核時鐘是要讀取RTC時間
來進行時間同步。并且在系統(tǒng)關機的時候?qū)⑾到y(tǒng)時間寫回RTC中進行同步。
硬件時鐘。當操作系統(tǒng)關機的時候,用這個來記錄時間,但是對于運行的系統(tǒng)是不用這個時間的。
另一個時間是 “System clock”也叫內(nèi)核時鐘或者軟件時鐘,是由軟件根據(jù)時間中斷來進行計數(shù)的,
內(nèi)核時鐘在系統(tǒng)關機的情況下是不存在的,所以,當操作系統(tǒng)啟動的時候,內(nèi)核時鐘是要讀取RTC時間
來進行時間同步。并且在系統(tǒng)關機的時候?qū)⑾到y(tǒng)時間寫回RTC中進行同步。
?
如前所述,Linux內(nèi)核與RTC進行互操作的時機只有兩個:
1) 內(nèi)核在啟動時從RTC中讀取啟動時的時間與日期;
2) 內(nèi)核在需要時將時間與日期回寫到RTC中。
1) 內(nèi)核在啟動時從RTC中讀取啟動時的時間與日期;
2) 內(nèi)核在需要時將時間與日期回寫到RTC中。
?
系統(tǒng)啟動時,內(nèi)核通過讀取RTC來初始化內(nèi)核時鐘,又叫墻上時間,該時間放在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:系統(tǒng)啟動時在哪讀取RTC的值并設置內(nèi)核時鐘進行時間同步的呢?
最有可能讀取RTC設置內(nèi)核時鐘的位置應該在arch/arm/kernel/time.c里的time_init函數(shù)內(nèi).
time.c為系統(tǒng)的時鐘驅(qū)動部分.time_init函數(shù)會在系統(tǒng)初始化時,由init/main.c里的start_kernel函數(shù)內(nèi)調(diào)用.X86架構就是在這里讀RTC值并初始化系統(tǒng)時鐘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()實際執(zhí)行的是時鐘驅(qū)動體系架構相關(具體平臺)部分定義的init函數(shù),若是s3c2410平臺,則執(zhí)行的為arch/arm/mach-s3c2410/time.c里定義的s3c2410_timer_init函數(shù).不過s3c2410_timer_init()也沒有讀RTC的代碼.整個時鐘驅(qū)動初始化的過程大致就執(zhí)行這些代碼.
既然在系統(tǒng)時鐘驅(qū)動初始化的過程中沒有讀RTC值并設置內(nèi)核時鐘,那會在哪設置呢?
?
我搜了一下,發(fā)現(xiàn)內(nèi)核好象只有在arch/cris/kernel/time.c里有RTC相關代碼,如下:?
/* arch/cris/kernel/time.c */
/* grab the time from the RTC chip */
//讀RTC的函數(shù)
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的函數(shù)
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);
}
?
這個函數(shù)會在update_xtime_from_cmos內(nèi)被調(diào)用:
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的函數(shù)
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這個文件和這兩個函數(shù)只是適用與X86?
ARM平臺啟動時并不走這邊.因此執(zhí)行不到這些函數(shù)。
那ARM平臺啟動時,系統(tǒng)是在哪讀RTC的值并對內(nèi)核時鐘(WallTime)進行初始化的呢?
ARM平臺啟動時并不走這邊.因此執(zhí)行不到這些函數(shù)。
那ARM平臺啟動時,系統(tǒng)是在哪讀RTC的值并對內(nèi)核時鐘(WallTime)進行初始化的呢?
?
已解決:
嵌入式Linux內(nèi)核(ARM)是在系統(tǒng)啟動時執(zhí)行/etc/init.d/hwclock.sh腳本,這個腳本會調(diào)用hwclock小程序讀取RTC的值并設置系統(tǒng)時鐘。
(換句話說,這要取決于你制作的文件系統(tǒng)里是否有這樣的腳本)
嵌入式Linux內(nèi)核(ARM)是在系統(tǒng)啟動時執(zhí)行/etc/init.d/hwclock.sh腳本,這個腳本會調(diào)用hwclock小程序讀取RTC的值并設置系統(tǒng)時鐘。
(換句話說,這要取決于你制作的文件系統(tǒng)里是否有這樣的腳本)
/* /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是這樣實現(xiàn)的:
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. 內(nèi)核如何更新RTC時鐘?
通過set_rtc函數(shù)指針指向的函數(shù),set_rtc在arch/arm/kernel/time.c內(nèi)
/* arch/arm/kernel/time.c */
/*
?* hook for setting the RTC's idea of the current time.
?*/
int (*set_rtc)(void);
通過set_rtc函數(shù)指針指向的函數(shù),set_rtc在arch/arm/kernel/time.c內(nèi)
/* arch/arm/kernel/time.c */
/*
?* hook for setting the RTC's idea of the current time.
?*/
int (*set_rtc)(void);
但是set_rtc函數(shù)指針在哪初始化的呢?set_rtc應該是和RTC驅(qū)動相關的函數(shù).
搜索kernel源碼后發(fā)現(xiàn),好象內(nèi)核其他地方并沒有對其初始化。待解決!
set_rtc在do_set_rtc內(nèi)調(diào)用
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里調(diào)用
/*
?* 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提供的體系架構無關的時鐘中斷處理函數(shù),通常會在體系架構相關的時鐘中斷處理函數(shù)內(nèi)調(diào)用它。如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提供的體系架構無關的時鐘中斷處理函數(shù),通常會在體系架構相關的時鐘中斷處理函數(shù)內(nèi)調(diào)用它。如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;
}
評論