Android时间机制

来源:互联网 发布:政府网站域名规范 编辑:程序博客网 时间:2024/06/06 03:48

在Android系统,通常使用System.currentTimeMillis()方法来获取计算系统的当前时间,而Calendar类中也是通过此方法来获取系统时间。下面就来分析一下System.currentTimeMillis()的具体实现。


currentTimeMillis()方法定义在$project/libcore/luni/src/main/java/java/lang/System.java中,

public static native long currentTimeMillis();

其为一个native方法,直接对应到jni层中的方法,该方法的映射在$project/dalvik/vm/native/java_lang_System.cpp中


const DalvikNativeMethod dvm_java_lang_System[] = {    { "arraycopy",          "(Ljava/lang/Object;ILjava/lang/Object;II)V",        Dalvik_java_lang_System_arraycopy },    { "currentTimeMillis",  "()J",        Dalvik_java_lang_System_currentTimeMillis },    { "identityHashCode",  "(Ljava/lang/Object;)I",        Dalvik_java_lang_System_identityHashCode },    { "mapLibraryName",     "(Ljava/lang/String;)Ljava/lang/String;",        Dalvik_java_lang_System_mapLibraryName },    { "nanoTime",  "()J",        Dalvik_java_lang_System_nanoTime },    { NULL, NULL, NULL },};static void Dalvik_java_lang_System_currentTimeMillis(const u4* args,    JValue* pResult){    struct timeval tv;    UNUSED_PARAMETER(args);    gettimeofday(&tv, (struct timezone *) NULL);    long long when = tv.tv_sec * 1000LL + tv.tv_usec / 1000;    RETURN_LONG(when);}

在该方法中主要是调用gettimeofday方法获取时间信息,该方法通过汇编映射到内核中($project/kernel/kernel/time.c),具体实现如下:


SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,struct timezone __user *, tz){if (likely(tv != NULL)) {struct timeval ktv;do_gettimeofday(&ktv);if (copy_to_user(tv, &ktv, sizeof(ktv)))return -EFAULT;}if (unlikely(tz != NULL)) {if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))return -EFAULT;}return 0;}

该方法中会使用do_gettimeofday来获取时间信息,然后通过copy_to_user将时间信息传递至用户层。do_gettimeofday定义在$project/kernel/kernel/time/timekeeping.c中。


void do_gettimeofday(struct timeval *tv){struct timespec now;getnstimeofday(&now);tv->tv_sec = now.tv_sec;tv->tv_usec = now.tv_nsec/1000;}EXPORT_SYMBOL(do_gettimeofday);

do_gettimeofday方法又调用了getnstimeofday方法。


void getnstimeofday(struct timespec *ts){unsigned long seq;s64 nsecs;WARN_ON(timekeeping_suspended);do {seq = read_seqbegin(&xtime_lock);*ts = xtime;nsecs = timekeeping_get_ns();/* If arch requires, add in gettimeoffset() */nsecs += arch_gettimeoffset();} while (read_seqretry(&xtime_lock, seq));timespec_add_ns(ts, nsecs);}EXPORT_SYMBOL(getnstimeofday);

getnstimeofday是执行了一个循环操作,在循环当中,首先通过read_seqbegin加一个读锁,然后使用xtime更新ts,最后调用timekeeping_get_ns方法获取时间流逝,而循环的条件使用read_seqretry方法来判断是否需要重新读。下面看timekeeping_get_ns方法:


static inline s64 timekeeping_get_ns(void){cycle_t cycle_now, cycle_delta;struct clocksource *clock;/* read clocksource: */clock = timekeeper.clock;cycle_now = clock->read(clock);/* calculate the delta since the last update_wall_time: */cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;/* return delta convert to nanoseconds using ntp adjusted mult. */return clocksource_cyc2ns(cycle_delta, timekeeper.mult,  timekeeper.shift);}

timekeeping_get_ns方法中,首先获取时钟源,然后调用时钟源的read方法,最后计算出流逝的时间。那我们再来研究一下时钟源是在哪里定义的,要解决这个问题,需要查看timekeeper是在哪里被初始化的。


在$project/kernel/init/main.c中有内核启动函数start_kernel,如下所示:


asmlinkage void __init start_kernel(void){......timekeeping_init();......}

timekeeping_init方法被定义在$project/kernel/time/timekeeping.c中:


void __init timekeeping_init(void){struct clocksource *clock;unsigned long flags;struct timespec now, boot;read_persistent_clock(&now);read_boot_clock(&boot);write_seqlock_irqsave(&xtime_lock, flags);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);update_rt_offset();total_sleep_time.tv_sec = 0;total_sleep_time.tv_nsec = 0;write_sequnlock_irqrestore(&xtime_lock, flags);}

该初始化函数通过clocksource_default_clock方法获取缺省的时钟,然后通过timekeeper_setup_internals方法初始化timekeeper变量。timekeeper_setup_internals如下所示:


static void timekeeper_setup_internals(struct clocksource *clock){cycle_t interval;u64 tmp, ntpinterval;timekeeper.clock = clock;clock->cycle_last = clock->read(clock);/* Do the ns -> cycle conversion first, using original mult */tmp = NTP_INTERVAL_LENGTH;tmp <<= clock->shift;ntpinterval = tmp;tmp += clock->mult/2;do_div(tmp, clock->mult);if (tmp == 0)tmp = 1;interval = (cycle_t) tmp;timekeeper.cycle_interval = interval;/* Go back from cycles -> shifted ns */timekeeper.xtime_interval = (u64) interval * clock->mult;timekeeper.xtime_remainder = ntpinterval - timekeeper.xtime_interval;timekeeper.raw_interval =((u64) interval * clock->mult) >> clock->shift;timekeeper.xtime_nsec = 0;timekeeper.shift = clock->shift;timekeeper.ntp_error = 0;timekeeper.ntp_error_shift = NTP_SCALE_SHIFT - clock->shift;/* * The timekeeper keeps its own mult values for the currently * active clocksource. These value will be adjusted via NTP * to counteract clock drifting. */timekeeper.mult = clock->mult;}

而clocksource_default_clock方法被定义在$project/kernel/kernel/time/jiffies.c中:


struct clocksource * __init __weak clocksource_default_clock(void){return &clocksource_jiffies;}

在$project/kernel/kernel/time/jiffies.c中,系统通过clocksource_register方法注册了一个时钟源至系统,而如果有其他时钟源,都可以使用此方法注册至系统,除此之外,系统还提供了clocksource_register_hz和clocksource_register_khz两个方法注册时钟源。在注册完成时钟源之后,系统会在clocksource_done_booting方法中通过clocksource_select方法选择最好的时钟源,clocksource_done_booting方法定义在$project/kernel/kernel/time/clocksource.c中。


static int __init clocksource_done_booting(void){mutex_lock(&clocksource_mutex);curr_clocksource = clocksource_default_clock();mutex_unlock(&clocksource_mutex);finished_booting = 1;/* * Run the watchdog first to eliminate unstable clock sources */clocksource_watchdog_kthread(NULL);mutex_lock(&clocksource_mutex);clocksource_select();mutex_unlock(&clocksource_mutex);return 0;}fs_initcall(clocksource_done_booting);static void clocksource_select(void){struct clocksource *best, *cs;if (!finished_booting || list_empty(&clocksource_list))return;/* First clocksource on the list has the best rating. */best = list_first_entry(&clocksource_list, struct clocksource, list);/* Check for the override clocksource. */list_for_each_entry(cs, &clocksource_list, list) {if (strcmp(cs->name, override_name) != 0)continue;/* * Check to make sure we don't switch to a non-highres * capable clocksource if the tick code is in oneshot * mode (highres or nohz) */if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) &&    tick_oneshot_mode_active()) {/* Override clocksource cannot be used. */printk(KERN_WARNING "Override clocksource %s is not "       "HRT compatible. Cannot switch while in "       "HRT/NOHZ mode\n", cs->name);override_name[0] = 0;} else/* Override clocksource can be used. */best = cs;break;}if (curr_clocksource != best) {printk(KERN_INFO "Switching to clocksource %s\n", best->name);curr_clocksource = best;timekeeping_notify(curr_clocksource);}}


信号源讲完后让我们再回到getnstimeofday函数中,在函数中使用timekeeping_get_ns获得时间的流逝,而之后会使用timespec_add_ns来将流逝时间加上之前的设置时间

static __always_inline void timespec_add_ns(struct timespec *a, u64 ns){a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns);a->tv_nsec = ns;}

而在getnstimeofday函数中,使用timespec_add_ns(ts, nsecs)来更新时间,而ts之前是有被赋值的,即*ts = xtime,而此处的xtime即为一个全局的时间变量,此xtime会在函数timekeeping_init中赋值

/* * timekeeping_init - Initializes the clocksource and common timekeeping values */void __init timekeeping_init(void){struct clocksource *clock;unsigned long flags;struct timespec now, boot;read_persistent_clock(&now);read_boot_clock(&boot);write_seqlock_irqsave(&xtime_lock, flags);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);update_rt_offset();total_sleep_time.tv_sec = 0;total_sleep_time.tv_nsec = 0;write_sequnlock_irqrestore(&xtime_lock, flags);}

其调用了read_persistent_clock函数对xtime进行了赋值,timekeeping_init函数是在内核启动的时候调用的

而read_persistent_clock是一个weak函数,其定义如下,其他地方可以重写此函数来确定每次开机时候的时间

void __attribute__((weak)) read_persistent_clock(struct timespec *ts){ts->tv_sec = 0;ts->tv_nsec = 0;}

至此,Android的时间机制讲述完毕。

crcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcr

Hufikyu的学习空间,欢迎大家提出问题,共同进步。

crcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcr


原创粉丝点击