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变量的值。