zl程序教程

您现在的位置是:首页 >  工具

当前栏目

hwclock和date源码分析

源码 分析 Date
2023-09-11 14:16:48 时间

一. 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硬件,只是读取或设置的墙上时间(软时间)        

 

三. 参考资料

  请看这里