time_t time (time t *result )是庫函數,最終會引起系統調用:
kernel\time\timekeeping.c
asmlinkage long sys_time(time_t __user * tloc)
{
time_t i = get_seconds();
if (tloc) {
if (put_user(i,tloc))
i = -EFAULT;
}
return i;
}
unsigned long get_seconds(void)
{
return xtime_cache.tv_sec;
}
xtime_cache和xtime變量也在這個檔案裡 .
xtime_cache是xtime變量的緩沖,更新xtime變量的時候,也會更新xtime_cache.
xtime變量在系統啟動的時候,通過讀rtc的值來初始化.在系統tick中斷的時候,會更新.
kernel\time\timekeeping.c
//phy3250沒有實作這個函數,是以有預設的函數,傳回0.
unsigned long __attribute__((weak)) read_persistent_clock(void)
{
return 0;
}
void __init timekeeping_init(void)
{
unsigned long flags;
unsigned long sec = read_persistent_clock();
write_seqlock_irqsave(&xtime_lock, flags);
ntp_init();
clock = clocksource_get_next();
clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
clock->cycle_last = clocksource_read(clock);
xtime.tv_sec = sec;
xtime.tv_nsec = 0;
set_normalized_timespec(&wall_to_monotonic,
-xtime.tv_sec, -xtime.tv_nsec);
update_xtime_cache(0);
total_sleep_time = 0;
write_sequnlock_irqrestore(&xtime_lock, flags);
}
xtime初始化為0.
系統啟動的時候,會調用函數:
drivers\rtc\hctosys.c
static int __init rtc_hctosys(void)
{
int err;
struct rtc_time tm;
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
if (rtc == NULL) {
printk("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
return -ENODEV;
}
err = rtc_read_time(rtc, &tm);
if (err == 0) {
err = rtc_valid_tm(&tm);
if (err == 0) {
struct timespec tv;
tv.tv_nsec = NSEC_PER_SEC >> 1;
rtc_tm_to_time(&tm, &tv.tv_sec);
do_settimeofday(&tv);
dev_info(rtc->dev.parent,
"setting system clock to "
"%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(unsigned int) tv.tv_sec);
}
else
dev_err(rtc->dev.parent,
"hctosys: invalid date/time\n");
}
else
dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
rtc_class_close(rtc);
return 0;
}
從rtc讀了時間之後,do_settimeofday函數又設定了xtime變量,同時也更新了xtime_cache.
int do_settimeofday(struct timespec *tv)
{
unsigned long flags;
time_t wtm_sec, sec = tv->tv_sec;
long wtm_nsec, nsec = tv->tv_nsec;
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
write_seqlock_irqsave(&xtime_lock, flags);
nsec -= __get_nsec_offset();
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
set_normalized_timespec(&xtime, sec, nsec);
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
update_xtime_cache(0);
clock->error = 0;
ntp_clear();
update_vsyscall(&xtime, clock);
write_sequnlock_irqrestore(&xtime_lock, flags);
clock_was_set();
return 0;
}
void update_xtime_cache(u64 nsec)
{
xtime_cache = xtime;
timespec_add_ns(&xtime_cache, nsec);
}
kernel\time.c
void set_normalized_timespec(struct timespec *ts, time_t sec, long nsec)
{
while (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
++sec;
}
while (nsec < 0) {
nsec += NSEC_PER_SEC;
--sec;
}
ts->tv_sec = sec;
ts->tv_nsec = nsec;
}
tick中斷的時候調用do_timer更新
kernel\timer.c
static inline void update_times(unsigned long ticks)
{
update_wall_time();
calc_load(ticks);
}
void do_timer(unsigned long ticks)
{
jiffies_64 += ticks;
update_times(ticks);
}
void update_wall_time(void)
{
cycle_t offset;
if (unlikely(timekeeping_suspended))
return;
#ifdef CONFIG_GENERIC_TIME
offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask;
#else
offset = clock->cycle_interval;
#endif
clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift;
while (offset >= clock->cycle_interval) {
clock->xtime_nsec += clock->xtime_interval;
clock->cycle_last += clock->cycle_interval;
offset -= clock->cycle_interval;
if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) {
clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift;
xtime.tv_sec++;
second_overflow();
}
clock->error += tick_length;
clock->error -= clock->xtime_interval << (NTP_SCALE_SHIFT - clock->shift);
}
clocksource_adjust(offset);
xtime.tv_nsec = (s64)clock->xtime_nsec >> clock->shift;
clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift;
update_xtime_cache(cyc2ns(clock, offset));
change_clocksource();
update_vsyscall(&xtime, clock);
}