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
- Android时间机制
- [android]时间自动更新机制
- Android 系统时间更新机制
- android自动更新时间时区机制
- android 中自动更新时间机制
- Android系统时间同步机制
- android的时间分发机制
- Android 时间更新机制研究
- Android Touch时间传递机制
- Android 系统时间自动更新机制
- Android系统时间更新机制
- Android 时间更新机制之网络更新时间
- Android 时间更新机制之RIL更新时间
- [Android 机制] 使用Ksoap2访问webservie,设置timeout时间
- Android学习——TouchEvent时间传递机制
- 【Android学习】View点击时间的分发机制(三)
- Linux时间机制-先转后看
- QT时间循环机制
- cmake交叉编译
- 如何用SQL语句获知SQL Agent服务是否启动
- [HTML5实现人工智能]小游戏《井字棋》发布,据说IQ上200才能赢
- 基于java的程序OutOfMemory问题的解决及Xms/Xmx/Xss的解释和应用
- oc基础
- Android时间机制
- ossec install.sh failed
- 南华大学暑假个人练习第一场 1006: Triangle
- iOS数据存储
- 远程MYSQL 1103错误 ERROR 1130: Host *.*.*.* is not allowed to connect to
- 基本数据结构之二叉查找树
- pycharm 教程(一)安装和首次使用
- VC __declspec关键字
- 20130718-"实时"竞价广告RTB要做到"即时"定向