Linux uptime实现详解

来源:互联网 发布:加工中心编程实例讲解 编辑:程序博客网 时间:2024/06/05 12:59

Linux uptime的实现

上源码

源码是长这个样子的, 小而美!

static int uptime_proc_show(struct seq_file *m, void *v){    struct timespec uptime;    struct timespec idle;    u64 idletime;    u64 nsec;    u32 rem;    int i;    idletime = 0;    for_each_possible_cpu(i)        idletime += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE];    get_monotonic_boottime(&uptime);    nsec = cputime64_to_jiffies64(idletime) * TICK_NSEC;    idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);    idle.tv_nsec = rem;    seq_printf(m, "%lu.%02lu %lu.%02lu\n",            (unsigned long) uptime.tv_sec,            (uptime.tv_nsec / (NSEC_PER_SEC / 100)),            (unsigned long) idle.tv_sec,            (idle.tv_nsec / (NSEC_PER_SEC / 100)));    return 0;}static int uptime_proc_open(struct inode *inode, struct file *file){    return single_open(file, uptime_proc_show, NULL);}static const struct file_operations uptime_proc_fops = {    .open       = uptime_proc_open,    .read       = seq_read,    .llseek     = seq_lseek,    .release    = single_release,};static int __init proc_uptime_init(void){    proc_create("uptime", 0, NULL, &uptime_proc_fops);    return 0;}fs_initcall(proc_uptime_init);

此时使用命令读取uptime是这样的:

cat /proc/uptime47.69 22.13

这就是刚刚开机不久后的开机时间47秒, CPU空闲22秒

增加调试信息

为了理清楚内核的时间的关系, 加了调试打印, 即get_xtime_and_monotonic_and_sleep_offset() 及其后面的seq_printf

static int uptime_proc_show(struct seq_file *m, void *v){    struct timespec uptime;    struct timespec idle;    u64 idletime;    u64 nsec;    u32 rem;    int i;    struct timespec xtime;    struct timespec wtime;    struct timespec stime;    idletime = 0;    for_each_possible_cpu(i)        idletime += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE];    get_xtime_and_monotonic_and_sleep_offset(&xtime, &wtime, &stime);    seq_printf(m, "xtime: %lu.%02lu\n",            (unsigned long) xtime.tv_sec,            (xtime.tv_nsec / (NSEC_PER_SEC / 100)));    seq_printf(m, "wtime: %lu.%02lu\n",            (unsigned long) wtime.tv_sec,            (wtime.tv_nsec / (NSEC_PER_SEC / 100)));    seq_printf(m, "stime: %lu.%02lu\n",            (unsigned long) stime.tv_sec,            (stime.tv_nsec / (NSEC_PER_SEC / 100)));    do_posix_clock_monotonic_gettime(&uptime);    seq_printf(m, "mtime: %lu.%02lu\n",            (unsigned long) uptime.tv_sec,            (uptime.tv_nsec / (NSEC_PER_SEC / 100)));    monotonic_to_bootbased(&uptime);    nsec = cputime64_to_jiffies64(idletime) * TICK_NSEC;    idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);    idle.tv_nsec = rem;    seq_printf(m, "%lu.%02lu %lu.%02lu\n",            (unsigned long) uptime.tv_sec,            (uptime.tv_nsec / (NSEC_PER_SEC / 100)),            (unsigned long) idle.tv_sec,            (idle.tv_nsec / (NSEC_PER_SEC / 100)));    return 0;}

此时的输出变成了这个样子:

xtime: 1262311849.44wtime: 3032655555.40stime: 0.00mtime: 108.85108.85 76.50

内核时间维护

  • 硬件时钟中断是时间更新的根本来源
  • 内核使用全局的一个数据结构来维护时间
static struct timekeeper timekeeper;

删除了无效的结构, 只关注主要的结构成员:

struct timekeeper {    ......    /* Current CLOCK_REALTIME time in seconds */    u64         xtime_sec;    /* Clock shifted nano seconds */    u64         xtime_nsec;        /*     * wall_to_monotonic is what we need to add to xtime (or xtime corrected     * for sub jiffie times) to get to monotonic time.  Monotonic is pegged     * at zero at system boot time, so wall_to_monotonic will be negative,     * however, we will ALWAYS keep the tv_nsec part positive so we can use     * the usual normalization.     *     * wall_to_monotonic is moved after resume from suspend for the     * monotonic time not to jump. We need to add total_sleep_time to     * wall_to_monotonic to get the real boot based time offset.     *     * - wall_to_monotonic is no longer the boot time, getboottime must be     * used instead.     */    struct timespec     wall_to_monotonic;    /* Offset clock monotonic -> clock realtime */    ktime_t         offs_real;    /* time spent in suspend */    struct timespec     total_sleep_time;    /* Offset clock monotonic -> clock boottime */    ktime_t         offs_boot;    /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */   ......
- **xtime_sec 和xtime_nsec:** 是根本, 记录的是当前的时间相对于1970-01-01 00:00:00 +0000 (UTC)的差值.  硬件时间更新即是此值.- **wall_to_monotonic:** 记录了一个差值(其实是一个负值), 通过xtime_sec + wall_to_monotonic 可计算出当前的开机时间以来的一个时间值(未记入计算机的时间), 即调用do_posix_clock_monotonic_gettime()返回的结果- **total_sleep_time: ** 记录了系统的休眠时间, 即通过调用do_posix_clock_monotonic_gettime()返回的结果

总结

其实内核主要维护xtime, wall_to_monotonic 和 total_sleep_time三个值即可完成对系统时间的正确维护, 当然这里没有考虑NTP时间同步的因素, 如果增加了NTP时间同步会繁杂一些.