hwclock和date源码分析
一. hwclock
1.1 hwclock源码在哪里?
util-linux 或者busybox
1.2 获取源码
git clone https://github.com/karelzak/util-linux.git
或
git clone git://git.busybox.net/busybox
1.3 hwclock的源码路径
sys-utils/hwclock.c
或
util-linux/hwclock.c
1.4 分析busybox中的util-linux/hwclock.c
if (opt & HWCLOCK_OPT_HCTOSYS) to_sys_clock(&rtcname, utc); else if (opt & HWCLOCK_OPT_SYSTOHC) from_sys_clock(&rtcname, utc); else if (opt & HWCLOCK_OPT_SYSTZ) set_system_clock_timezone(utc); else /* default HWCLOCK_OPT_SHOW */ show_clock(&rtcname, utc);
1.4.1 分析from_sys_clock
static void from_sys_clock(const char **pp_rtcname, int utc) { #if 1 struct timeval tv; struct tm tm_time; int rtc; rtc = rtc_xopen(pp_rtcname, O_WRONLY); gettimeofday(&tv, NULL); /* Prepare tm_time */ if (sizeof(time_t) == sizeof(tv.tv_sec)) { if (utc) gmtime_r((time_t*)&tv.tv_sec, &tm_time); else localtime_r((time_t*)&tv.tv_sec, &tm_time); } else { time_t t = tv.tv_sec; if (utc) gmtime_r(&t, &tm_time); else localtime_r(&t, &tm_time); } #else ... #endif tm_time.tm_isdst = 0; xioctl(rtc, RTC_SET_TIME, &tm_time); if (ENABLE_FEATURE_CLEAN_UP) close(rtc); }
总结: hwclock将会从rtc硬件(寄存器)中读取时间或往rtc硬件中写入时间,与rtc硬件息息相关。
二. date
2.1 date的源码在哪里
coreutils
2.2 获取源码
git clone https://github.com/coreutils/coreutils.git
或
wget https://ftp.gnu.org/pub/gnu/coreutils/coreutils-8.31.tar.xz
2.3 date的源码路径
src/date.c
2.4 如何获取时间
使用gettime接口(这是glibc中的接口)
2.5 gettime接口又是怎么实现的呢?
/* Get the system time into *TS. */ void gettime (struct timespec *ts) { #if defined CLOCK_REALTIME && HAVE_CLOCK_GETTIME clock_gettime (CLOCK_REALTIME, ts); #else struct timeval tv; gettimeofday (&tv, NULL); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000; #endif }
2.6 从以上源码中可以看出时间要么通过clock_gettime获取,要么通过gettimeofday获取
这两个接口的差异为:
clock_gettime提供纳秒级精度,而后者提供微秒级精度
2.7 如何设置时间呢?
使用settime接口(这是glibc中的接口)
2.8 settime接口又是怎么实现的呢?
/* Set the system time. */ int settime (struct timespec const *ts) { #if defined CLOCK_REALTIME && HAVE_CLOCK_SETTIME { int r = clock_settime (CLOCK_REALTIME, ts); if (r == 0 || errno == EPERM) return r; } #endif #if HAVE_SETTIMEOFDAY { struct timeval tv; tv.tv_sec = ts->tv_sec; tv.tv_usec = ts->tv_nsec / 1000; return settimeofday (&tv, 0); } #elif HAVE_STIME /* This fails to compile on OSF1 V5.1, due to stime requiring a 'long int*' and tv_sec is 'int'. But that system does provide settimeofday. */ return stime (&ts->tv_sec); #else errno = ENOSYS; return -1; #endif }
2.9 从以上settime的源码可以看出,要么通过clock_settime接口(linux内核中的系统调用)设置,要么通过settimeofday接口(linux内核中的系统调用)设置
2.10 那么linux内核中clock_settime是如何实现的呢?
请看下面的系统调用定义
#define __NR_clock_settime64 404 __SYSCALL(__NR_clock_settime64, sys_clock_settime)
2.10.1 __SYSCALL是如何定义的?(arch/arm64/kernel/sys.c)
#define __SYSCALL(nr, sym) [nr] = __arm64_##sym,
2.10.2 展开后即为
[404] = __arm64_sys_clock_settime,
2.10.3 看一下__arm64_sys_clock_settime在哪里?先看看kernel/time/posix-timers.c文件中的定义
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, const struct __kernel_timespec __user *, tp) { const struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec64 new_tp; if (!kc || !kc->clock_set) return -EINVAL; if (get_timespec64(&new_tp, tp)) return -EFAULT; return kc->clock_set(which_clock, &new_tp); }
2.10.4 SYSCALL_DEFINE2是如何定义的? (include/linux/syscalls.h)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINEx(x, sname, ...) \
SYSCALL_METADATA(sname, x, __VA_ARGS__) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#define SYSCALL_METADATA(sname, nb, ...) \ static const char *types_##sname[] = { \ __MAP(nb,__SC_STR_TDECL,__VA_ARGS__) \ }; \ static const char *args_##sname[] = { \ __MAP(nb,__SC_STR_ADECL,__VA_ARGS__) \ }; \ SYSCALL_TRACE_ENTER_EVENT(sname); \ SYSCALL_TRACE_EXIT_EVENT(sname); \ static struct syscall_metadata __used \ __syscall_meta_##sname = { \ .name = "sys"#sname, \ .syscall_nr = -1, /* Filled in at boot */ \ .nb_args = nb, \ .types = nb ? types_##sname : NULL, \ .args = nb ? args_##sname : NULL, \ .enter_event = &event_enter_##sname, \ .exit_event = &event_exit_##sname, \ .enter_fields = LIST_HEAD_INIT(__syscall_meta_##sname.enter_fields), \ }; \ static struct syscall_metadata __used \ __attribute__((section("__syscalls_metadata"))) \ *__p_syscall_meta_##sname = &__syscall_meta_##sname;
#define __SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long __arm64_sys##name(const struct pt_regs *regs); \ ALLOW_ERROR_INJECTION(__arm64_sys##name, ERRNO); \ static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ asmlinkage long __arm64_sys##name(const struct pt_regs *regs) \ { \ return __se_sys##name(SC_ARM64_REGS_TO_ARGS(x,__VA_ARGS__)); \ } \ static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ __MAP(x,__SC_TEST,__VA_ARGS__); \ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ return ret; \ } \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
2.10.5 展开一下看看
SYSCALL_DEFINEx(2, _clock_settime,__VAARGS__)
-> SYSCALL_METADATA(_clock_settime,2,__VA_ARGS__) \
__SYSCALL_DEFINEx(2, _clock_settime, __VA_ARGS__)
-> static const char *types__clock_settime[] = { \
__MAP(2,__SC_STR_TDECL,__VA_ARGS__) \
}; \
static const char *args__clock_settime[] = { \
__MAP(2,__SC_STR_ADECL,__VA_ARGS__) \
}; \
SYSCALL_TRACE_ENTER_EVENT(_clock_settime); \
SYSCALL_TRACE_EXIT_EVENT(_clock_settime); \
static struct syscall_metadata __used \
__syscall_meta__clock_settime = { \
.name = "sys_clock_settime", \
.syscall_nr = -1, /* Filled in at boot */ \
.nb_args = 2, \
.types = 2 ? types__clock_settime : NULL, \
.args = 2 ? args__clock_settime : NULL, \
.enter_event = &event_enter__clock_settime, \
.exit_event = &event_exit__clock_settime, \
.enter_fields = LIST_HEAD_INIT(__syscall_meta__clock_settime.enter_fields), \
}; \
static struct syscall_metadata __used \
__attribute__((section("__syscalls_metadata"))) \
*__p_syscall_meta__clock_settime = &__syscall_meta__clock_settime; \
asmlinkage long __arm64_sys_clock_settime(const struct pt_regs *regs); \
ALLOW_ERROR_INJECTION(__arm64_sys_clock_settime, ERRNO); \
static long __se_sys_clock_settime(__MAP(x,__SC_LONG,__VA_ARGS__)); \
static inline long __do_sys_clock_settime(__MAP(x,__SC_DECL,__VA_ARGS__)); \
asmlinkage long __arm64_sys_clock_settime(const struct pt_regs *regs) \
{ \
return __se_sys_clock_settime(SC_ARM64_REGS_TO_ARGS(x,__VA_ARGS__)); \
} \
static long __se_sys_clock_settime(__MAP(x,__SC_LONG,__VA_ARGS__)) \
{ \
long ret = __do_sys_clock_settime(__MAP(x,__SC_CAST,__VA_ARGS__)); \
__MAP(x,__SC_TEST,__VA_ARGS__); \
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
return ret; \
} \
static inline long __do_sys_clock_settime(__MAP(x,__SC_DECL,__VA_ARGS__))
{
const struct k_clock *kc = clockid_to_kclock(which_clock);
struct timespec64 new_tp;
if (!kc || !kc->clock_set)
return -EINVAL;
if (get_timespec64(&new_tp, tp))
return -EFAULT;
return kc->clock_set(which_clock, &new_tp);
}
2.10.6 发现最后执行的是结构体kc中的clock_set函数,那么是哪个clock_set呢?
看一下kernel/time/posix-timers.c中的clock_set域
static const struct k_clock clock_realtime = { .clock_getres = posix_get_hrtimer_res, .clock_get = posix_clock_realtime_get, .clock_set = posix_clock_realtime_set, .clock_adj = posix_clock_realtime_adj, .nsleep = common_nsleep, .timer_create = common_timer_create, .timer_set = common_timer_set, .timer_get = common_timer_get, .timer_del = common_timer_del, .timer_rearm = common_hrtimer_rearm, .timer_forward = common_hrtimer_forward, .timer_remaining = common_hrtimer_remaining, .timer_try_to_cancel = common_hrtimer_try_to_cancel, .timer_arm = common_hrtimer_arm, };
那就看看posix_clock_realtime_get()
static int posix_clock_realtime_set(const clockid_t which_clock, const struct timespec64 *tp) { return do_sys_settimeofday64(tp, NULL); }
总结: date不论更新时间还是设置时间都未涉及到rtc硬件,只是读取或设置的墙上时间(软时间)
三. 参考资料
请看这里