在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