linux time keeping解读

本文介绍了Linux系统时间记录子系统的基本概念,包括systemtime、walltime等,并详细分析了timekeeping初始化过程及其如何选择和切换时钟源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简介

linux time keeping顾名思义,即linux 系统时间记录子系统,用于记录linux系统内核的几种时间。下面对linux系统的这几张时间做简要介绍:

1) system time
A monotonically increasing value that represents the amount of time the system has been running. 单调增长的系统运行时间, 可以通过time source, xtime及wall_to_monotonic计算出来.
2) wall time
A value representing the the human time of day, as seen on a wrist-watch. Realtime时间: xtime.
3) time source
A representation of a free running counter running at a known frequency, usually in hardware, e.g GPT. 可以通过clocksource->read()得到counter值
4) tick
A periodic interrupt generated by a hardware-timer, typically with a fixed interval defined by HZ: jiffies

这些time之间互相关联, 互相可以转换.
system_time = xtime + cyc2ns(clock->read() - clock->cycle_last) + wall_to_monotonic;
real_time = xtime + cyc2ns(clock->read() - clock->cycle_last)
也就是说real time是从1970年开始到现在的nanosecond, 而system time是系统启动到现在的nanosecond.
这两个是最重要的时间, 由此hrtimer可以基于这两个time来设置过期时间. 所以引入两个clock base:

CLOCK_REALTIME: base在实际的wall time
CLOCK_MONOTONIC: base在系统运行system time

CLOCK_REALTIME 调用ktime_get_real()来获得真实时间, 该函数用上面提到的等式计算出realtime.
CLOCK_MONOTONIC 调用ktime_get()、system_time的等式获得monotonic time.

下面以x86架构,内核版本2.6.34.10对这部分代码进行分析。

二、time keeping初始化

time keeping是在start_kernel里面被调的,start_kernel的功能在这里不做展开介绍。

void __init timekeeping_init(void)
{
 struct clocksource *clock;
 unsigned long flags;
 struct timespec now, boot;

 read_persistent_clock(&now);  //通过读rtc寄存器获取当前墙上时间
 read_boot_clock(&boot);  //获取系统boot时的时间,x86下只是简单的做清零动作。

 write_seqlock_irqsave(&xtime_lock, flags);

 ntp_init();//Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正。这里对其进行初始化。

 clock = clocksource_default_clock(); //获取默认的时钟源,这里是clocksource_jiffies,其enable方法是空的。
 if (clock->enable)
  clock->enable(clock);
 timekeeper_setup_internals(clock); //设置timekeeper内部时钟源时钟。实际上是对全局的timekeeper变量的各个成员进行逐一赋值。

 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_xtime_cache(0);
 total_sleep_time.tv_sec = 0;
 total_sleep_time.tv_nsec = 0;
 write_sequnlock_irqrestore(&xtime_lock, flags);
}

 

可见,系统启动的时候,使用的clocksource是clocksource_jiffies,它的精度不高,在系统启动过程的后期,新的时钟源可以替换掉它。

那么,它是如何被替换的呢?

答案是,不同的时钟源驱动中调clocksource_register,注册新的时钟源的时候就完成这个动作。

int clocksource_register(struct clocksource *cs)
{
 /* calculate max idle time permitted for this clocksource */
 cs->max_idle_ns = clocksource_max_deferment(cs);

 mutex_lock(&clocksource_mutex);
 clocksource_enqueue(cs);
 clocksource_select();
 clocksource_enqueue_watchdog(cs);
 mutex_unlock(&clocksource_mutex);
 return 0;
}

 

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);
 }
}

clocksource_select选择一种最好的可用的时钟源。首先遍历clocksource_list链表,若其是高精度时钟源,或者非oneshot mode,则选择当前时钟源。遍历完后,若curr_clocksource != best,则调timekeeping_notify安装新的时钟源。

void timekeeping_notify(struct clocksource *clock)
{
 if (timekeeper.clock == clock)
  return;
 stop_machine(change_clocksource, clock, NULL);
 tick_clock_notify();
}

核心部分是change_clocksource:

static int change_clocksource(void *data)
{
 struct clocksource *new, *old;

 new = (struct clocksource *) data;

 timekeeping_forward_now();
 if (!new->enable || new->enable(new) == 0) {
  old = timekeeper.clock;
  timekeeper_setup_internals(new);
  if (old->disable)
   old->disable(old);
 }
 return 0;
}

最终调 timekeeper_setup_internals完成新的时钟源的替换工作。

x86下,调用clocksource_register进行注册的时钟源有hpet、Apb_timer、I8253、kvm_clock、tsc等众多的时钟源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值