linux时间子系统(三)
2.2.3 timekeeper初始化
void __init timekeeping_init(void)
{
struct clocksource *clock;
unsigned long flags;
struct timespec now, boot;
read_persistent_clock(&now);
read_boot_clock(&boot);
raw_spin_lock_irqsave(&xtime_lock, flags);
write_seqcount_begin(&xtime_seq);
ntp_init();
clock = clocksource_default_clock();
if (clock->enable)
clock->enable(clock);
timekeeper_setup_internals(clock);
xtime.tv_sec = now.tv_sec;
xtime.tv_nsec = now.tv_nsec;
raw_time.tv_sec = 0;
raw_time.tv_nsec = 0;
if (boot.tv_sec == 0 && boot.tv_nsec == 0) {
boot.tv_sec = xtime.tv_sec;
boot.tv_nsec = xtime.tv_nsec;
}
set_normalized_timespec(&wall_to_monotonic,
-boot.tv_sec, -boot.tv_nsec);
total_sleep_time.tv_sec = 0;
total_sleep_time.tv_nsec = 0;
write_seqcount_end(&xtime_seq);
raw_spin_unlock_irqrestore(&xtime_lock, flags);
}
从初始化函数中可以看到,内核首先通过read_persistent_clock函数,从RTC硬件中获取RTC time。如果不存在RTC硬件,则RTC time被初始化为0。之后,初始化xtime,raw_time,wall_to_monotonic和total_sleep_time。
2.2.4 时间更新
2.2.4.1 xtime和raw_time更新
static void timekeeping_forward_now(void)
{
cycle_t cycle_now, cycle_delta;
struct clocksource *clock;
s64 nsec;
clock = timekeeper.clock;
cycle_now = clock->read(clock);
cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
clock->cycle_last = cycle_now;
nsec = clocksource_cyc2ns(cycle_delta, timekeeper.mult,
timekeeper.shift);
/* If arch requires, add in gettimeoffset() */
nsec += arch_gettimeoffset();
timespec_add_ns(&xtime, nsec);
nsec = clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift);
timespec_add_ns(&raw_time, nsec);
}
在本函数中,首先计算当前时刻与上一次调用read回调函数时刻clocksoure计数值的差值,记为cycle_delta。之后,在计算xtime的调整时长时,使用的是timekeeper结构中的mult和shift字段,而在计算raw_time的调整时长时,使用的是clocksource的mult和shift字段。因timekeeper的mult字段会被ntp调整,所以说xtime受ntp调整的影响而raw_time不受ntp调整的影响。
2.2.4.2 total_sleep_time/monotonic time
static void __timekeeping_inject_sleeptime(struct timespec *delta)
{
xtime = timespec_add(xtime, *delta);
wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta);
total_sleep_time = timespec_add(total_sleep_time, *delta);
}
在休眠结束时会调用__timekeeping_inject_sleeptime来调整时间。由于xtime是墙上时间,所以必须加上休眠时间。monotonic time不受休眠时间的影响,所以需要在wall_to_monotonic中减去相应的休眠时间,这样xtime与wall_to_monotonic的和所表示的monotonic time的值就没有发生跳变。在最后,更新total_sleep_time的值。
由于monotonic time的值是xtime与wall_to_monotonic之和,所以除了休眠时间和使用do_settimeofday调整时间时需要调整wall_to_monotonic外,其他时候,monotonic time随xtime增长而增长。所以大部分时间我们不需要调整wall_to_monotonic变量的值。