仅作为内核代码中时间管理模块的笔记,3.10内核,很乱,不喜勿喷。

先有time,后有timer。

常用的time结构有哪些?除了大名鼎鼎的jiffies和jiffies64之外,还有常用的一些结构如下:

ktime_t 经常用在timer中,
union ktime {
s64 tv64;
#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR)
struct {
# ifdef __BIG_ENDIAN
s32 sec, nsec;
# else
s32 nsec, sec;
# endif
} tv;
#endif
}; typedef union ktime ktime_t; /* Kill this */

经常用在fs中的timespec,低一点精度的timeval,以及时区结构timezone。主要用来做时间戳等。

struct timespec {
__kernel_time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
struct timeval {
    __kernel_time_t     tv_sec;     /* seconds */
    __kernel_suseconds_t    tv_usec;    /* microseconds */
};
struct timezone {
    int tz_minuteswest; /* minutes west of Greenwich */
    int tz_dsttime; /* type of dst correction */
};
 

这些结构之间的常用转换函数:

/* convert a timespec to ktime_t format: */
static inline ktime_t timespec_to_ktime(struct timespec ts)
{
return ktime_set(ts.tv_sec, ts.tv_nsec);
} /* convert a timespec64 to ktime_t format: */
static inline ktime_t timespec64_to_ktime(struct timespec64 ts)
{
return ktime_set(ts.tv_sec, ts.tv_nsec);
} /* convert a timeval to ktime_t format: */
static inline ktime_t timeval_to_ktime(struct timeval tv)
{
return ktime_set(tv.tv_sec, tv.tv_usec * NSEC_PER_USEC);
} /* Map the ktime_t to timespec conversion to ns_to_timespec function */
#define ktime_to_timespec(kt) ns_to_timespec((kt).tv64) /* Map the ktime_t to timespec conversion to ns_to_timespec function */
#define ktime_to_timespec64(kt) ns_to_timespec64((kt).tv64) /* Map the ktime_t to timeval conversion to ns_to_timeval function */
#define ktime_to_timeval(kt) ns_to_timeval((kt).tv64) /* Convert ktime_t to nanoseconds - NOP in the scalar storage format: */
#define ktime_to_ns(kt) ((kt).tv64)

比如有时候自己不想那么高精度的时间戳怎么办呢?内核还提供了这个函数,取到秒级,最方便的是这个函数还被导出了,很好用。

unsigned long get_seconds(void)
{
struct timekeeper *tk = &timekeeper; return tk->xtime_sec;
}
EXPORT_SYMBOL(get_seconds);

还有个有趣的问题是,这个时间的维护,精度要更高的话,就需要用顺序锁去读取 timekeeper 变量。

struct timespec current_kernel_time(void)
{
struct timekeeper *tk = &timekeeper;
struct timespec64 now;
unsigned long seq; do {
seq = read_seqcount_begin(&timekeeper_seq); now = tk_xtime(tk);
} while (read_seqcount_retry(&timekeeper_seq, seq)); return timespec64_to_timespec(now);
}
EXPORT_SYMBOL(current_kernel_time);

好了,time除了用来做时间戳之前,另外一个大的应用就是timer的超时时间了。在描述timer之前,有必要描述linux 关于时间管理的几个大的概念,

低精度的timer定义:

crash> tvec_base
struct tvec_base {
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
unsigned long next_timer;
unsigned long active_timers;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
unsigned long all_timers;
}

低精度定时器结构:

struct timer_list {---------------------低精度定时器结构,
struct list_head entry;-------------用这个挂入到时间轮的链表中,与高精度的rb_node类比
unsigned long expires;--------------超期时间
struct tvec_base *base;-------------指向某个cpu的 tvec_base
void (*function)(unsigned long);----回调
unsigned long data;
int slack;
int start_pid;
void *start_site;
char start_comm[];
}

常用的配套函数有:add_timer,mod_timer,add_timer_on(指定cpu添加timer),del_timer,DEFINE_TIMER,setup_timer等,这些在协议栈代码里面非常常见,一般用来等待超时。既然是超时,那么对时间精度要求就不那么高了,所以实现的时候,用了著名的定时器轮。

add_timer的流程和mod_timer的流程差不多,先判断该timer是不是pending,pending的意思就是从定时器轮已经摘取了,可能正在执行中,它的特征就是 该timer的 entry的next是否为NULL

static inline int timer_pending(const struct timer_list * timer)
{
return timer->entry.next != NULL;
}

一句话总结:正等待被调度执行的定时器对象就是pending的。如果一个定时器不是pending的,那么肯定在定时器轮上。

接下来,自然要先从原来的位置摘除,

static inline void detach_timer(struct timer_list *timer, bool clear_pending)
{
struct list_head *entry = &timer->entry; debug_deactivate(timer); __list_del(entry->prev, entry->next);-----如果timer以前没加入在定时器轮中,则这个啥都不做。
if (clear_pending)
entry->next = NULL;
entry->prev = LIST_POISON2;
}

然后根据这个定时器的超时时间,加入到定时器轮中对应的vec中,主要改动两个,一个是timer的base,还有一个是timer的entry的所处的位置。

crash> p tvec_bases:
per_cpu(tvec_bases, ) = $ = (struct tvec_base *) 0xffffffff81ea71c0 <boot_tvec_bases>
crash> tvec_bases
PER-CPU DATA TYPE:
struct tvec_base *tvec_bases;
PER-CPU ADDRESSES:
[]: ffff8827dca13948
[]: ffff8827dca53948
[]: ffff8827dca93948
[]: ffff8827dcad3948
[]: ffff8827dcb13948
[]: ffff8827dcb53948
[]: ffff8827dcb93948
。。。。

这里还有一个细节,就是timer的base,由于这个是一个指针,所以至少是4字节对齐的,也就是后面两位肯定为0,被用来做标记了,当从timer中取这个base指针的时候,就需要将这两

位处理掉,不能直接用来解引用,否则会出现访问错误。

由于低精度的定时器是以jiffies来作为最低精度的,所以精度有限制,但随着硬件以及多媒体发展的实时性较高的要求,后来,又引入了高精度定时器。它是以纳秒为精度的。高精度定时器结构如下:

crash> hrtimer
struct hrtimer {
struct timerqueue_node node;---------------------------用来插入到红黑树中
ktime_t _softexpires;----------------------------------超期的时间
enum hrtimer_restart (*function)(struct hrtimer *);----回调函数,肯定都有,不过它的返回值只有两个
struct hrtimer_clock_base *base;-----------------------和低精度定时器类似,也有指向一个percpu的base的一个指针,不过base结构与低精度定时器time_list不同
unsigned long state;
int start_pid;
void *start_site;
char start_comm[];
}
SIZE: 它指向的base是percpu的 hrtimer_bases,注意和低精度定时器的base相区别,因为低精度的base是percpu的 tvec_base

而高精度定时器的索引,也不是低精度那个vec管理,而是红黑树来管理的。

crash> timerqueue_head
struct timerqueue_head {
struct rb_root head;
struct timerqueue_node *next;
}
SIZE:
crash> hrtimer_clock_base
struct hrtimer_clock_base {
struct hrtimer_cpu_base *cpu_base;
int index;
clockid_t clockid;
struct timerqueue_head active;------------管理同类型的hrtimer的红黑树封装
ktime_t resolution;
ktime_t (*get_time)(void);
ktime_t rh_reserved_softirq_time;
ktime_t offset;
} crash> hrtimer_cpu_base
struct hrtimer_cpu_base {
raw_spinlock_t lock;
unsigned int active_bases;
unsigned int clock_was_set;
ktime_t expires_next;
int hres_active;
int hang_detected;
unsigned long nr_events;
unsigned long nr_retries;
unsigned long nr_hangs;
ktime_t max_hang_time;
struct hrtimer_clock_base clock_base[];-------------它的地位,和时间轮的vec相当,是用来管理timer的,通过clockid来分类 int cpu;
    int in_hrtirq; 
}

相应的percpu管理结构,与低精度的tvec_base相对比:

crash> hrtimer_bases------------整个hrtimer_interrupt都是以这个变量为基础
PER-CPU DATA TYPE:
struct hrtimer_cpu_base hrtimer_bases;
PER-CPU ADDRESSES:
[]: ffff8827dca13960
[]: ffff8827dca53960
[]: ffff8827dca93960
[]: ffff8827dcad3960
[]: ffff8827dcb13960
[]: ffff8827dcb53960
[]: ffff8827dcb93960
[]: ffff8827dcbd3960
[]: ffff8827dcc13960
[]: ffff8827dcc53960
[]: ffff8827dcc93960
。。。。 crash> p hrtimer_bases:
per_cpu(hrtimer_bases, ) = $ = {
lock = {
raw_lock = {
val = {
counter =
}
}
},
active_bases = ,
clock_was_set = ,
expires_next = {
tv64 =
},
hres_active = ,
hang_detected = ,
nr_events = ,
nr_retries = ,
nr_hangs = ,
max_hang_time = {
tv64 =
},
clock_base = {{
cpu_base = 0xffff8827dca13960,
index = ,
clockid = ,
active = {
head = {
rb_node = 0xffff881677e57e88
},
next = 0xffffe8d01d20f220
},
resolution = {
tv64 =
},
get_time = 0xffffffff810f0670 <ktime_get>,
rh_reserved_softirq_time = {
tv64 =
},
offset = {
tv64 =
}
}, {
cpu_base = 0xffff8827dca13960,
index = ,
clockid = ,
active = {
head = {
rb_node = 0xffff881c433fbd38
},
next = 0xffff884a744a7d38
},
resolution = {
tv64 =
},
get_time = 0xffffffff810f0ad0 <ktime_get_real>,
rh_reserved_softirq_time = {
tv64 =
},
offset = {
tv64 =
}
}, {
cpu_base = 0xffff8827dca13960,
index = ,
clockid = ,
active = {
head = {
rb_node = 0x0
},
next = 0x0
},
resolution = {
tv64 =
},
get_time = 0xffffffff810f0c40 <ktime_get_boottime>,
rh_reserved_softirq_time = {
tv64 =
},
offset = {
tv64 =
}
}, {
cpu_base = 0xffff8827dca13960,
index = ,
clockid = ,
active = {
head = {
rb_node = 0x0
},
next = 0x0
},
resolution = {
tv64 =
},
get_time = 0xffffffff810f08f0 <ktime_get_clocktai>,
rh_reserved_softirq_time = {
tv64 =
},
offset = {
tv64 =
}
}},
cpu = ,
in_hrtirq =
}

两类定时器模块的初始化,在start_kernel中,

asmlinkage void __init start_kernel(void)
{
。。。。
init_timers();//定时器模块初始化
hrtimers_init();//高精度定时器模块初始化
。。。。
}

对比了两类定时器的定义,从定时器的执行再来对比一下,会加深印象。

对于低精度来说,

void __init init_timers(void)
{
int err; /* ensure there are enough low bits for flags in timer->base pointer */
BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK); err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
init_timer_stats(); BUG_ON(err != NOTIFY_OK);
register_cpu_notifier(&timers_nb);
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}
在收到TIMER_SOFTIRQ 之后,run_timer_softirq-->__run_timers,这个函数会执行所有到期的定时的回调函数。执行回调的时候都持有 base->lock 这把自旋锁,所以也要求执行函数不能耗时太多。
 
对于高精度定时器来说,由于有两种模式,所以需要单独说明调用流程,

如果是处于低分辨率模式,则会在周期性的 update_process_times-->run_local_timers-->hrtimer_run_queues-->__hrtimer_run_queues 来把这些高精度定时器回调来执行;

update_process_times 调用 run_local_timers 来触发TIMER_SOFTIRQ软中断,run_timer_softirq负责调用__run_timers处理 TIMER_SOFTIRQ软中断。

 run_local_timers  除了触发软中断,还调用   hrtimer_run_queues();看能否从低分辨率定时器切换到高分辨率。
run_local_timers | -->hrtimer_run_queues 负责分辨率切换--->hrtimer_switch_to_hres-->tick_setup_sched_timer
                           |-->raise_softirq(TIMER_SOFTIRQ)

如果是处于高精度模式,则虽然周期性的 update_process_times-->run_local_timers-->hrtimer_run_queues 会执行,但不会调用 __hrtimer_run_queues ,而是在 hrtimer_interrupt

函数中调用 __hrtimer_run_queues->__run_hrtimer 来完成定时器的调用。

调用链如下:

hrtimer_interrupt-->__hrtimer_run_queues-->__run_hrtimer-->执行回调。

这点和网上的不一致,因为网上大多是2.6的内核描述,其实在哪处理不是很关键,主要是理解数据结构和调用。

总结一下:

  • 每个cpu有一个tvec_base结构;

  • tvec_base结构管理着5个不同超时时间的数组,它采用的基准时间是jiffies。

  • 加入时间轮的时候,通过timer_list的超时时间,来指定它vec,

  • 时间轮,按到期时间进行处理,第一轮vec处理完毕,会在第二轮中取一个数组元素填充第一轮的256个到底的元素,

  • 它通过__run_timers来执行所有到期的低精度定时器

  • 每个cpu有一个hrtimer_cpu_base结构;
  • hrtimer_cpu_base结构管理着3种不同的时间基准系统的hrtimer,分别是:实时时间,启动时间和单调时间;它的基准时间是纳秒。
  • 每种时间基准系统通过它的active字段(timerqueue_head结构指针),指向它们各自的红黑树;
  • 红黑树上,按到期时间进行排序,最先到期的hrtimer位于最左下的节点,并被记录在active.next字段中;
  • 3中时间基准的最先到期时间可能不同,所以,它们之中最先到期的时间被记录在hrtimer_cpu_base的expires_next字段中。
 

有一点需要注意,高精度定时器要生效,意味着我们要有高精度的时钟源,那么当没有这么高精度的时钟源的时候,高精度定时器的运转,则精度会降低。

说到时钟源:在我的机器上,3.10的内核,封装了一个结构,叫clocksource如下:

crash> list clocksource.list -H clocksource_list
ffffffff81a273c0
ffffffff81a2bb40
ffffffff81aebb80
ffffffff81eb5980
ffffffff81a52c40
crash> clocksource ffffffff81a273c0
struct clocksource {
read = 0xffffffff81032e20 <read_tsc>,-----------这个成员的位置放到第一个,因为它最频繁使用,和2.6.18系列版本不一样,大家定义结构的时候把最常使用的放前面,便于cache命中
cycle_last = ,
mask = ,
mult = ,
shift = ,
max_idle_ns = ,
maxadj = ,
archdata = {
vclock_mode =
},
name = 0xffffffff819217b4 "tsc",
list = {
next = 0xffffffff81a2bb78 <clocksource_hpet+>,
prev = 0xffffffff81a52c30 <clocksource_list>
},
rating = ,----------------精度最高
enable = 0x0,
disable = 0x0,
flags = ,
suspend = 0x0,
resume = 0x0,
owner = 0x0
}
crash> clocksource ffffffff81a2bb40
struct clocksource {
read = 0xffffffff81062430 <read_hpet>,
cycle_last = ,
mask = ,
mult = ,
shift = ,
max_idle_ns = ,
maxadj = ,
archdata = {
vclock_mode =
},
name = 0xffffffff818ff927 "hpet",
list = {
next = 0xffffffff81aebbb8 <clocksource_acpi_pm+>,
prev = 0xffffffff81a273f8 <clocksource_tsc+>
},
rating = ,
enable = 0x0,
disable = 0x0,
flags = ,
suspend = 0x0,
resume = 0xffffffff810619e0 <hpet_resume_counter>,
owner = 0x0
}
crash> clocksource ffffffff81aebb80
struct clocksource {
read = 0xffffffff8153cb10 <acpi_pm_read>,
cycle_last = ,
mask = ,
mult = ,
shift = ,
max_idle_ns = ,
maxadj = ,
archdata = {
vclock_mode =
},
name = 0xffffffff8191e0b6 "acpi_pm",
list = {
next = 0xffffffff81eb59b8 <refined_jiffies+>,
prev = 0xffffffff81a2bb78 <clocksource_hpet+>
},
rating = ,
enable = 0x0,
disable = 0x0,
flags = ,
suspend = 0x0,
resume = 0x0,
owner = 0x0
}
crash> clocksource ffffffff81eb5980
struct clocksource {
read = 0xffffffff810f3290 <jiffies_read>,
cycle_last = ,
mask = ,
mult = ,
shift = ,
max_idle_ns = ,
maxadj = ,
archdata = {
vclock_mode =
},
name = 0xffffffff8191e0fb "refined-jiffies",
list = {
next = 0xffffffff81a52c78 <clocksource_jiffies+>,
prev = 0xffffffff81aebbb8 <clocksource_acpi_pm+>
},
rating = ,------------------------精度最低
enable = 0x0,
disable = 0x0,
flags = ,
suspend = 0x0,
resume = 0x0,
owner = 0x0
}
crash> clocksource ffffffff81a52c40
struct clocksource {
read = 0xffffffff810f3290 <jiffies_read>,
cycle_last = ,
mask = ,
mult = ,
shift = ,
max_idle_ns = ,
maxadj = ,
archdata = {
vclock_mode =
},
name = 0xffffffff8191e103 "jiffies",
list = {
next = 0xffffffff81a52c30 <clocksource_list>,
prev = 0xffffffff81eb59b8 <refined_jiffies+>
},
rating = ,
enable = 0x0,
disable = 0x0,
flags = ,
suspend = 0x0,
resume = 0x0,
owner = 0x0
}

用户可以通过 手工来切换clocksource,比如我的环境上有tsc,hpet,acpi_pm三个可用的clocksource(这个比crash中列的少一些)

cat /sys/devices/system/clocksource/clocksource0/available_clocksource
tsc hpet acpi_pm
[root@localhost ~]# cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc
[root@localhost ~]# cat /sys/devices/system/clocksource/clocksource0/unbind_clocksource
cat: /sys/devices/system/clocksource/clocksource0/unbind_clocksource: Permission denied
[root@localhost ~]# cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc
[root@localhost ~]# ls -alrt /sys/devices/system/clocksource/clocksource0/current_clocksource
-rw-r--r-- root root Oct : /sys/devices/system/clocksource/clocksource0/current_clocksource
[root@localhost ~]# echo hpet > /sys/devices/system/clocksource/clocksource0/current_clocksource
[root@localhost ~]# cat /sys/devices/system/clocksource/clocksource0/current_clocksource
hpet
[root@localhost ~]# echo tsc > /sys/devices/system/clocksource/clocksource0/current_clocksource
切换之后会有打印,有时候也可以在message中看到内核自动切换的打印。

[44890.290544] Switched to clocksource hpet
[44902.121090] Switched to clocksource tsc

介绍完时钟源的定义和使用,有必要介绍下一个重要概念,时钟事件设备。

时间事件设备允许注册一个事件,在未来一个指定的时间点上发生,但与定时器实现相比,它只能存储一个事件。

举一个clock_event_device 的例子:

clock_event_device ffff8827dcf11140
struct clock_event_device {
event_handler = 0xffffffff810b9890 <hrtimer_interrupt>,
set_next_event = 0xffffffff81053df0 <lapic_next_deadline>,
set_next_ktime = 0x0,
next_event = {
tv64 =
},
max_delta_ns = ,
min_delta_ns = ,
mult = ,
shift = ,
mode = CLOCK_EVT_MODE_ONESHOT,
features = ,-------------------------------------------属性,为2说明是oneshot模式
retries = ,
broadcast = 0xffffffff81053e30 <lapic_timer_broadcast>,
set_mode = 0xffffffff81054620 <lapic_timer_setup>,
suspend = 0x0,
resume = 0x0,
min_delta_ticks = ,
max_delta_ticks = ,
name = 0xffffffff818fefdd "lapic",------------------事件设备的名称
rating = ,
irq = -,
bound_on = ,
cpumask = 0xffffffff816e7c60 <cpu_bit_bitmap+>,
list = {
next = 0xffff8857bc2d11d8,
prev = 0xffff8827dcf511d8
},
owner = 0x0
}
/*
* Clock event features
*/
#define CLOCK_EVT_FEAT_PERIODIC 0x000001
#define CLOCK_EVT_FEAT_ONESHOT 0x000002
#define CLOCK_EVT_FEAT_KTIME 0x000004
/*
* x86(64) specific misfeatures:
*
* - Clockevent source stops in C3 State and needs broadcast support.
* - Local APIC timer is used as a dummy device.
*/
#define CLOCK_EVT_FEAT_C3STOP 0x000008
#define CLOCK_EVT_FEAT_DUMMY 0x000010 /*
* Core shall set the interrupt affinity dynamically in broadcast mode
*/
#define CLOCK_EVT_FEAT_DYNIRQ 0x000020 /*
* Clockevent device is based on a hrtimer for broadcast
*/
#define CLOCK_EVT_FEAT_HRTIMER 0x000080

每个时钟硬件设备注册一个时钟设备tick_device 和一个时钟事件设备。

struct tick_device {
    struct clock_event_device *evtdev;
    enum tick_device_mode mode;
};
可以看出,时钟设备就是时钟事件设备的简单封装。
 

为了精度,系统兼容了两套定时器,一套是时间轮的低精度定时器,一种是高精度的hrtimer。定时器软中断调用 hrtimer_run_queues 来处理高分辨率定时器队列,哪怕底层时钟事件设备只提供了低分辨率,也是如此。这使得可以使用现存的框架,而无需关注时钟的分辨率。

为了节能,系统又引入了tickless模型,也就是nohz模型,其实就是将原来周期性的tick,变为按需触发,对于需要模拟tick的周期性函数,则由相应的cpu来完成,其他cpu如果没事可以

休息。

nohz_mode目前包含三种模式,一种是未开启nohz,一种是系统工作于低分辨率模式下的动态时钟,一种是系统工作于高精度模式下的动态时钟。

struct tick_sched {
struct hrtimer sched_timer;---用于高分辨率模式下,模拟周期时钟的一个timer
unsigned long check_clocks;
enum tick_nohz_mode nohz_mode;---包含三种模式,
ktime_t last_tick;
ktime_t next_tick;
int inidle;
int tick_stopped;
unsigned long idle_jiffies;
unsigned long idle_calls;
unsigned long idle_sleeps;
int idle_active;
ktime_t idle_entrytime;
ktime_t idle_waketime;
ktime_t idle_exittime;
ktime_t idle_sleeptime;
ktime_t iowait_sleeptime;
ktime_t sleep_length;
unsigned long last_jiffies;
u64 next_timer;
ktime_t idle_expires;
int do_timer_last;
};
 
crash> tick_sched
struct tick_sched {
struct hrtimer sched_timer;
unsigned long check_clocks;
enum tick_nohz_mode nohz_mode;
ktime_t last_tick;
ktime_t next_tick;
int inidle;
int tick_stopped;
unsigned long idle_jiffies;
unsigned long idle_calls;
unsigned long idle_sleeps;
int idle_active;
ktime_t idle_entrytime;
ktime_t idle_waketime;
ktime_t idle_exittime;
ktime_t idle_sleeptime;
ktime_t iowait_sleeptime;
ktime_t sleep_length;
unsigned long last_jiffies;
u64 next_timer;
ktime_t idle_expires;
int do_timer_last;
}

tick_sched 中收集的统计信息通过/proc/timer_list 导出到用户层。

crash> tick_cpu_sched
PER-CPU DATA TYPE:
struct tick_sched tick_cpu_sched;
PER-CPU ADDRESSES:
[]: ffff8827dca13f20
[]: ffff8827dca53f20
[]: ffff8827dca93f20
[]: ffff8827dcad3f20
。。。。。。。。。。。。。 crash> p tick_cpu_sched:
per_cpu(tick_cpu_sched, ) = $ = {
sched_timer = {
node = {
node = {
__rb_parent_color = ,
rb_right = 0x0,
rb_left = 0x0
},
expires = {
tv64 =
}
},
_softexpires = {
tv64 =
},
function = 0xffffffff810f9170 <tick_sched_timer>,
base = 0xffff8827dca139a0,
state = ,
start_pid = ,
start_site = 0xffffffff810f95c2 <tick_nohz_stop_sched_tick+>,
start_comm = "swapper/0\000\000\000\000\000\000"
},
check_clocks = ,
nohz_mode = NOHZ_MODE_HIGHRES,
last_tick = {
tv64 =
},
next_tick = {
tv64 =
},
inidle = ,
tick_stopped = ,
idle_jiffies = ,
idle_calls = ,
idle_sleeps = ,
idle_active = ,
idle_entrytime = {
tv64 =
},
idle_waketime = {
tv64 =
},
idle_exittime = {
tv64 =
},
idle_sleeptime = {
tv64 =
},
iowait_sleeptime = {
tv64 =
},
sleep_length = {
tv64 =
},
last_jiffies = ,
next_timer = ,
idle_expires = {
tv64 =
},
do_timer_last =
}

对时钟的禁用是按cpu指定的,一般来说,所有cpu都空闲的概率还是比较低的。

crash> p tick_next_period
tick_next_period = $ = {
tv64 =
}
crash> p tick_next_period
tick_next_period = $ = {
tv64 =
}
crash> p tick_next_period
tick_next_period = $ = {
tv64 =
}
crash> p tick_next_period
tick_next_period = $ = {
tv64 =
}
crash> p last_jiffies_update
last_jiffies_update = $ = {
tv64 =
}
crash> p last_jiffies_update
last_jiffies_update = $ = {
tv64 =
}
crash> p last_jiffies_update
last_jiffies_update = $ = {
tv64 =
}

时间相关系统调用及外部设置:

adjtimex 系统调用,NTP设置,

内核的工作模式:

  1. 没有动态时钟的低分辨率系统,总是用周期时钟。这时不会支持单触发模式

  2. 启用了动态时钟的低分辨率系统,将以单触发模式是用时钟设备

  3. 高分辨率系统总是用单触发模式,无论是否启用了动态时钟特性

  4. 高分辨率时钟系统,每个cpu会使用一个hrtimer来模拟周期时钟,提供tick,毕竟精度高的要模拟精度低的比较容易,同时又能纳入自己的高分辨率框架。模拟的函数为:tick_sched_timer

非广播时最终的处理函数:

高分辨率动态时钟:hrtimer_interrupt

高分辨率周期时钟:hrtimer_interrupt

低分辨率动态时钟:tick_nohz_handler

低分辨率周期时钟:tick_handle_periodic

广播时最终的处理函数:

高分辨率动态时钟:tick_handle_oneshot_broadcast

高分辨率周期时钟:tick_handle_oneshot_broadcast

低分辨率动态时钟:tick_handle_oneshot_broadcast

低分辨率周期时钟:tick_handle_periodic_broadcast

参考资料:

linux 3.10内核源码
原文:https://blog.csdn.net/goodluckwhh/article/details/9048565

 

linux 时间相关的一些总结的更多相关文章

  1. 【转载】Linux时间相关结构与函数

    1 时间的获取 在程序当中, 我们经常要输出系统当前的时间,比如日志文件中的每一个事件都要记录其产生时间.在 C 语言中获取当前时间的方法有以下几种,它们所获得的时间精度从秒级到纳秒,各有所不同. 表 ...

  2. Linux - 时间相关命令 - ntpdate, date, hwclock

    1. 概述 最近也不知道写啥了, 把之前的老文档整理一下, 凑个数什么的 配置时间这种工作, 偶尔还是要用一下 主要描述 3 个命令的简单适用 ntpdate hwlock 2. ntpdate 1. ...

  3. linux 时间相关

    CentOS7 正确修改时区方法 ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

  4. Linux编程---线程

    首先说一下线程的概念.事实上就是运行在进程的上下文环境中的一个运行流.普通进程仅仅有一条运行流,可是线程提供了多种运行的路径并行的局面. 同一时候,线程还分为核心级线程和用户级线程.主要差别在属于核内 ...

  5. Linux 驱动开发

    linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...

  6. Linux服务器时间相关命令记录

    前言 以往安装服务器时间都是正常,但是最近服务器的时间经常出现问题,所以在安装配置完成服务器之后需要对服务器的时间进行测试,如果服务器时间异常,那么当程序去取系统时间的时候就会出现问题. 时间相关命令 ...

  7. Linux shell 日期,时间相关的命令

    在shell脚本中,经常要用到跟获取日期相关的东西,这里记录一下Linux shell 获取日期的方法 获取当前日期:today=`date +"%Y-%m-%d"` 获取昨天的日 ...

  8. Linux之时间相关操作20170607

    一.Linux常用时间相关函数 -asctime,ctime,getttimeofday,gmtime,localtime,mktime,settimeofday,time asctime       ...

  9. Linux 驱动框架---驱动中的时间相关

    内核中的时间 Linux 系统内核对于时间的管理依赖于硬件,硬件按一定的周期产生中断,周期由内核的一个配置值HZ决定在系统启动时会将定时器配置为HZ值指定的频率产生中断:同时内核和维护一个64位(X8 ...

随机推荐

  1. webview之总结2

    21,js与androud交互之javascript调用本地之方法一(接口类): ========= 21,js与androud交互之javascript调用本地之方法一(接口类): Android4 ...

  2. [UE4]自定义函数,快速增加输入参数的一种方法

  3. [UE4]枚举Enum和Switch Enum

  4. MySQL数据库order by 奇慢无比

    今天遇到个奇葩的问题, sql 数据量很大 有where 和order by,不加order by 速度很快,加了就很慢 一.首先我们对这条sql执行查询计划: explain select t.or ...

  5. 初始Golang

    Golang初识 字节跳动也就是我们常说的今日头条 1.今日头条基于Go语言构建千亿级微服务的实践 今日头条当前后端服务超过80%的流量是跑在Go构建的服务上 微服务数量超过100个 高峰QPS超过7 ...

  6. SqlServer存储过程输出参数

    if exists(select 1 from sysobjects where name='P_PreOrderInfo') drop Procedure P_PreOrderInfo go Cre ...

  7. 06 I/O重定向与管道符

    首先,我们知道我们的计算机结构,在第一节的时候已经介绍过了,CPU进行数据运算,同时控制器负责指令的发送,而内存则是数据存储的地方,CPU读取的数据均从内存中调取.电脑除了CPU和内存外,我们还有I/ ...

  8. 对KVM虚拟机进行cpu pinning配置的方法

    这篇文章主要介绍了对KVM虚拟机进行cpu pinning配置的方法,通过文中的各种virsh命令可进行操作,需要的朋友可以参考下 首先需求了解基本的信息 1 宿主机CPU特性查看 使用virsh n ...

  9. mac OS 安装maven遇到问题e45: 'readonly' option is set

    1.下载 Maven, 并解压到某个目录.例如/Users/yintingting/apache-maven-3.3.9 2.打开Terminal,输入以下命令,设置Maven classpath v ...

  10. 安装python的jupyter notebook工具

    jupyter notebook是一个通过网页运行python的工具 支持分段的python运行,并能直观的查看结果 支持多python环境运行,需要加装(conda) 安装步骤 1.安装python ...