Android时间机制


在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


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值