内核版本:v3.4.xxx

一、前言

内核提供了方便查看当前系统TickDevice、活动的Timer列表以及Timer使用的统计信息。

内核分别用两个节点来表示TimerList和Timer统计信息。

一个是/proc/timer_list,打印per_cpu的hrtimer_bases信息以及基于此的timer列表,包括三种时钟MONOTONIC/REALTIME/BOOTTIME;以及Broadcast Tick Device和Per CPU Tick Device信息。

另一个是/proc/timer_stats,需要echo 1 > /proc/timer_stats打开,echo 0 /proc/timer_stats关闭。cat /proc/timer_stats可以获取统计信息。

二、背景介绍

Per CPU TickDevice是维护当前CPU Tick和hrtimer的基础,只有工作在OneShot模式下才能实现hrtimer。在CPU进入cpuidle后,Per CPU TickDevice可能会被关闭,这时候可以打开Broadcast Tick作为唤醒源。超时后,就会将系统从cpuidle退出,进入正常工作模式打开Per CPU TickDevice。

hrtimer有三种时钟基准,这三种时钟基准对应不同的时间获取函数,但主要是基于TimeKeeping的统计。

三、认识timer_list

kernel/time/timer_list.c中init_timer_list_procfs创建/proc/timer_list节点。

static int timer_list_show(struct seq_file *m, void *v)
{
  u64 now = ktime_to_ns(ktime_get());
  int cpu;

  SEQ_printf(m, "Timer List Version: v0.6\n");------------------------------------------------------------------(1)
  SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
  SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);

  for_each_online_cpu(cpu)----------------------------------------------------------------------------------------(2)
    print_cpu(m, cpu, now);

  SEQ_printf(m, "\n");
  timer_list_show_tickdevices(m);--------------------------------------------------------------------------------(3)

  return 0;
}

(1)打印概况信息,hrtimer基准时钟个数和系统MONOTONIC时间。

(2)这里是per_cpu信息,按照hrtimer_cpu_base->hrtimer_clock_base->hrtimer层级关系打印。

(3)打印Broadcast TickDevice和per_cpu的TickDevice设备信息。

下面根据一个示例来分析:

Timer List Version: v0.6
HRTIMER_MAX_CLOCK_BASES: 3
now at 86212951044093 nsecs

cpu: 0-----------------------print_cpuCPU0的信息如下,从clock0到clock2
clock 0:---------------------clock0作为MONOTONIC使用ktime_get获取当前时间,是timerkeeper的xtime和wall_to_monotonic之和。
.base: c0531950
.index: 0
.resolution: 1 nsecs
.get_time: ktime_get
.offset: 0 nsecs------------和MONOTONIC相比的offset。
active timers:--------------print_active_timers打印所有的hrtimer,按照时间排列,timerqueue_iterate_next读取rbtree的next。

--依次是:序列号、hrtimer、timer状态、hrtimer超时函数、进程名/进程号

--超时绝对时间和相对时间。
#0: <c0532620>, tick_sched_timer, S:01, hrtimer_start_range_ns, swapper/0
# expires at 86212955000000-86212955000000 nsecs [in 3955907 to 3955907 nsecs]
#1: <c11d1f20>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, xxx_log_agent/1326
# expires at 86212957402735-86212957452735 nsecs [in 6358642 to 6408642 nsecs]
#2: <c1289ab8>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, xxx_ipsec_proxy/1349
# expires at 86213073541650-86213076541650 nsecs [in 122497557 to 125497557 nsecs]
#3: <c11d3f20>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, xxx_log_agent/1327
# expires at 86213291925439-86213291975439 nsecs [in 340881346 to 340931346 nsecs]
#4: <c105df20>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, xxx_log_agent/1324
# expires at 86213768121728-86213768171728 nsecs [in 817077635 to 817127635 nsecs]
#5: <c108bf20>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, xxx_log_agent/1325
# expires at 86213768152245-86213768202245 nsecs [in 817108152 to 817158152 nsecs]
#6: <c104fab8>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, at_ctl/785
# expires at 86214382959375-86214387959375 nsecs [in 1431915282 to 1436915282 nsecs]
#7: <c12bbf20>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, dm_ci/1365
# expires at 86248421289453-86248421339453 nsecs [in 35470245360 to 35470295360 nsecs]
#8: <c11d7ab8>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, udhcpd/1147
# expires at 86409238207327-86409338207327 nsecs [in 196287163234 to 196387163234 nsecs]
#9: <c1aadd90>, it_real_fn, S:01, hrtimer_start, SoftTimerLoop/782
# expires at 86421323022854-86421323022854 nsecs [in 208371978761 to 208371978761 nsecs]
#10: <c11dff20>, hrtimer_wakeup, S:01, hrtimer_start_range_ns, netmonitor/1313
# expires at 86421323999417-86421324049417 nsecs [in 208372955324 to 208373005324 nsecs]
clock 1:
.base: c0531988
.index: 1
.resolution: 1 nsecs
.get_time: ktime_get_real
.offset: 946759810014430770 nsecs
active timers:
#0: <c0563760>, alarmtimer_fired, S:01, hrtimer_start, sample/1354
# expires at 946846022969655783-946846022969655783 nsecs [in 946759810018611690 to 946759810018611690 nsecs]
clock 2:
.base: c05319c0
.index: 2
.resolution: 1 nsecs
.get_time: ktime_get_boottime
.offset: 706858435 nsecs
active timers:

-------------------------------------------------------------------hrtimer_cpu_base部分,参照结构体解释----------------------------------------------------------------------
.expires_next : 86212955000000 nsecs---------CPU层次看hrtimer信息,可以在上面找到对应的最近一次expires
.hres_active : 1
.nr_events : 4648009
.nr_retries : 4467
.nr_hangs : 0
.max_hang_time : 0 nsecs

---------------------------------------------------------------------tick_sched部分,参照结构体解释-----------------------------------------------------------------------------
.nohz_mode : 2--------------------------------------nohz_mode对应NOHZ_MODE_INACTIVE(0)、NOHZ_MODE_LOWRES(1)、NOHZ_MODE_HIGHRES(2)。
.idle_tick : 86212945000000 nsecs
.tick_stopped : 0
.idle_jiffies : 17182588
.idle_calls : 4632242
.idle_sleeps : 4573348
.idle_entrytime : 86212940790186 nsecs
.idle_waketime : 86212940790186 nsecs
.idle_exittime : 86212940790186 nsecs
.idle_sleeptime : 84593571589272 nsecs
.iowait_sleeptime: 0 nsecs
.last_jiffies : 17182588
.next_jiffies : 17182604
.idle_expires : 86213020000000 nsecs
jiffies: 17182590

Tick Device: mode: 1-----------------------Broadcast TickDevice
Broadcast device
Clock Event Device: <NULL>
tick_broadcast_mask: 00000000
tick_broadcast_oneshot_mask: 00000000

Tick Device: mode: 1---------------------per_cpu TickDevice,详细信息参照clock_event_device结构体解释。
Per CPU device: 0
Clock Event Device: xxx_ap_timer0
max_delta_ns: 660764185443
min_delta_ns: 15385
mult: 13958644
shift: 32
mode: 3
next_event: 86212955000000 nsecs
set_next_event: xxx_timer_set_next_event
set_mode: xxx_timer_set_mode
event_handler: hrtimer_interrupt
retries: 165992

四、认识timer_stats

kernel/time/timer_stats.c中init_tstas_procfs创建了/proc/timer_stats节点。统计细心

在每次hrtimer超时函数中都会进行统计,必须打开CONFIG_TIMER_STATS才有效。

__hrtimer_start_range_ns-->raise_softirq_irqoff-->HRTIMER_SOFTIRQ-->run_hrtimer_softirq-->hrtimer_interrupt-->__run_hrtimer-->timer_stats_account_hrtimer

timer_stats_account_hrtimer是执行统计信息的主体,这里面有一个timer_stats_active开关。是通过echo [0|1] > /proc/timer_stats进行开关。

void timer_stats_update_stats(void *timer, pid_t pid, void *startf,
void *timerf, char *comm,
unsigned int timer_flag)
{
  /*
  * It doesn't matter which lock we take:
  */
  raw_spinlock_t *lock;
  struct entry *entry, input;
  unsigned long flags;

  if (likely(!timer_stats_active))----------------------------------------是否打开开关
    return;

  lock = &per_cpu(tstats_lookup_lock, raw_smp_processor_id());

  input.timer = timer;---------------------------------------------------填充entry结构体
  input.start_func = startf;
  input.expire_func = timerf;
  input.pid = pid;
  input.timer_flag = timer_flag;

  raw_spin_lock_irqsave(lock, flags);
  if (!timer_stats_active)
    goto out_unlock;

  entry = tstat_lookup(&input, comm);-------------------------------在tstat_hash_table中查找input,找到count递增;没找到新增一个entry插入hash表。
  if (likely(entry))
    entry->count++;
  else
    atomic_inc(&overflow_count);

  out_unlock:
    raw_spin_unlock_irqrestore(lock, flags);
}

显示的地方在tstats_show,所有的统计信息存放在static struct entry entries[MAX_ENTRIES]结构体数组中,当前entry数目通过nr_entries来记录。

static int tstats_show(struct seq_file *m, void *v)
{
  struct timespec period;
  struct entry *entry;
  unsigned long ms;
  long events = 0;
  ktime_t time;
  int i;

  mutex_lock(&show_mutex);
  /*
  * If still active then calculate up to now:
  */
  if (timer_stats_active)
  time_stop = ktime_get();

  time = ktime_sub(time_stop, time_start);

  period = ktime_to_timespec(time);
  ms = period.tv_nsec / 1000000;

  seq_puts(m, "Timer Stats Version: v0.2\n");
  seq_printf(m, "Sample period: %ld.%03ld s\n", period.tv_sec, ms);
  if (atomic_read(&overflow_count))
    seq_printf(m, "Overflow: %d entries\n",
  atomic_read(&overflow_count));

  for (i = 0; i < nr_entries; i++) {----------------------遍历entries数组,数组内容在每次hrtimer中断处理中进行更新。
    entry = entries + i;
    if (entry->timer_flag & TIMER_STATS_FLAG_DEFERRABLE) {
    seq_printf(m, "%4luD, %5d %-16s ",
      entry->count, entry->pid, entry->comm);
    } else {
     seq_printf(m, " %4lu, %5d %-16s ",
      entry->count, entry->pid, entry->comm);
    }

    print_name_offset(m, (unsigned long)entry->start_func);
    seq_puts(m, " (");
    print_name_offset(m, (unsigned long)entry->expire_func);
    seq_puts(m, ")\n");

    events += entry->count;
  }

  ms += period.tv_sec * 1000;
  if (!ms)
    ms = 1;

  if (events && period.tv_sec)
    seq_printf(m, "%ld total events, %ld.%03ld events/sec\n",
      events, events * 1000 / ms,
      (events * 1000000 / ms) % 1000);
  else
    seq_printf(m, "%ld total events\n", events);

  mutex_unlock(&show_mutex);

  return 0;
}

一个实例分析:

Timer Stats Version: v0.2
Sample period: 42.997 s

--依次是:timer count、进程号、进程名称、超时函数、启动timer函数
2112, 1300 xxx_log_agent hrtimer_start_range_ns (hrtimer_wakeup)
43, 1299 xxx_log_agent hrtimer_start_range_ns (hrtimer_wakeup)
43, 1298 xxx_log_agent hrtimer_start_range_ns (hrtimer_wakeup)
43, 1301 xxx_log_agent hrtimer_start_range_ns (hrtimer_wakeup)
5, 1881 sample hrtimer_start (alarmtimer_fired)
116, 0 swapper hrtimer_start_range_ns (tick_sched_timer)
14, 1355 xxx_ipsec_proxy hrtimer_start_range_ns (hrtimer_wakeup)
28, 0 swapper hrtimer_start (tick_sched_timer)
17D, 0 swapper cpufreq_interactive_timer_resched.constprop.8 (cpufreq_interactive_timer)
8, 794 at_ctl hrtimer_start_range_ns (hrtimer_wakeup)
6, 0 swapper run_timer_softirq (sync_supers_timer_fn)
4, 0 swapper DWC_TIMER_SCHEDULE (timer_callback)
4, 0 swapper DWC_TIMER_SCHEDULE (timer_callback)
4, 0 swapper DWC_TIMER_SCHEDULE (timer_callback)
4, 0 swapper br_hello_timer_expired (br_hello_timer_expired)
3, 613 zx_wdt_thread msleep (process_timeout)
2D, 4 kworker/0:0 neigh_periodic_work (delayed_work_timer_fn)
2D, 4 kworker/0:0 neigh_periodic_work (delayed_work_timer_fn)
2, 1366 adbd DWC_TIMER_SCHEDULE (timer_callback)
1, 0 swapper igmp6_group_queried (igmp6_timer_handler)
1, 1371 dm_ci hrtimer_start_range_ns (hrtimer_wakeup)
1, 0 swapper igmp6_group_queried (igmp6_timer_handler)
1, 0 swapper igmp6_group_queried (igmp6_timer_handler)
2464 total events, 57.306 events/sec

五、使用

1.通过timer_stats可以知道系统的timer使用程度,一定程度上反映了进程的活跃状态。

2.timer_list可以知道系统TickDevice相关设备信息。

3.还可以知道timer状态,以及即将发生的Timer。

4.系统相关信息尤其是tick_sched(系统调度jiffies、idle、iowait等)和hrtimer_cpu_base(retries、hang)部分。

Linux时间子系统之一:认识timer_list和timer_stats和使用的更多相关文章

  1. Linux时间子系统专题汇总

    关于Linux时间子系统有两个系列文章讲的非常好,分别是WowoTech和DroidPhone. 还有两本书分别是介绍: Linux用户空间时间子系统<Linux/UNIX系统编程手册>的 ...

  2. Linux时间子系统之(二):软件架构

    专题文档汇总目录 Notes:从框架上讲解了时间子系统,从底向上包括CPU Local TImer.Global Counter.Clock Souce/Clock Events模块管理.Tick D ...

  3. Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

    转自:http://blog.csdn.net/droidphone/article/details/8074892 上一篇文章,我介绍了传统的低分辨率定时器的实现原理.而随着内核的不断演进,大牛们已 ...

  4. Linux时间子系统之五:低分辨率定时器的原理和实现

    专题文档汇总目录 Notes:低精度timer在内核中的数据结构以及API接口:低精度timer精巧高效的分组,使用cascade进行定时器移位,组内Timer FIFO:低精度Timer的初始化流程 ...

  5. Linux时间子系统之(一):时间的基本概念

    专题文档汇总目录 Notes:Linux时间基准点:Linux时间和broken-down time(struct tm):不同精度的时间表示time_t.timeval.timespec. 原文地址 ...

  6. Linux时间子系统之(三):用户空间接口函数

    专题文档汇总目录 Notes:用户空间时间相关接口函数: 类型 API 精度 说明 时间 time stime time_t 精度为秒级 逐渐要被淘汰.需要定义__ARCH_WANT_SYS_TIME ...

  7. Linux时间子系统之(四):timekeeping

    专题文档汇总目录 Notes:timekeeping模块的狠心数据结构是timekeeper,它维护了系统不同类型时钟的时间值,并且介绍了获取不同类型时钟时间的函数. clocksource切换通过c ...

  8. Linux时间子系统之(五):POSIX Clock

    专题文档汇总目录 Notes: 本章主要介绍了若干种类的静态时钟,这些时钟都可以通过k_clock表示,注册到posix_clocks中.这些都是静态时钟,可以分为三大类:各种REALTIME时钟.带 ...

  9. Linux时间子系统之(六):POSIX timer

    专题文档汇总目录 Notes:首先讲解了POSIX timer的标识(唯一识别).POSIX Timer的组织(管理POSIX Timer).内核中如何抽象POSIX Timer:然后分析了POSIX ...

  10. Linux时间子系统之(十二):periodic tick

    专题文档汇总目录 Notes:TickDevice模式,以及clocckevent设备.TickDevice设备的初始化,TickDevice是如何加入到系统中的.周期性Tick的产生. 原文地址:L ...

随机推荐

  1. Media Player Classic - HC 源代码分析 7:详细信息选项卡(CPPageFileInfoDetails)

    ===================================================== Media Player Classic - HC 源代码分析系列文章列表: Media P ...

  2. 苹果新的编程语言 Swift 语言进阶(十六)--泛型

    泛型允许你定义一个宽松.可重用的函数或者类型,使用泛型能够避免代码的重复,也能以更清楚和抽象的方式来表达程序的意图. 泛型是Swift语言提供的强大功能之一,Swift提供的许多标准库都使用了泛型来创 ...

  3. C++之默认参数

    C++可以为不指定参数提供默认值.一旦给一个参数赋了默认值,后面的所有参数,也都必须为默认值,并且默认值的类型也必须正确,默认值可以在原型或者函数定义中给出,但是不能两个位置同时给出. 接下来我们上代 ...

  4. CRF资料

    与最大熵模型相似,条件随机场(Conditional random fields,CRFs)是一种机器学习模型,在自然语言处理的许多领域(如词性标注.中文分词.命名实体识别等)都有比较好的应用效果.条 ...

  5. LeetCode(64)- Min Stack

    题目: Design a stack that supports push, pop, top, and retrieving the minimum element in constant time ...

  6. DTN学习,theONE模拟器网络相关资料整理

    下面是一个百度空间的: http://hi.baidu.com/jensenliao 博客园的一篇博客:theONE模拟器简介(主要讲述,软件配置,软件结构) http://www.cnblogs.c ...

  7. CALayer简介

    一.什么是CALayer * 在iOS系统中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮.一个文本标签.一个文本输入框.一个图标等等,这些都是UIView. * 其实UIView之所以 ...

  8. javascript随机一个1-9的数字

    window.onload=function(){        var oTxt=document.getElementById('txt');        for(i=1;i<=200;i ...

  9. 《深入理解JAVA虚拟机》笔记1

    java程序运行时的内存空间,按照虚拟机规范有下面几项: )程序计数器 指示下条命令执行地址.当然是线程私有,不然线程怎么能并行的起来. 不重要,占内存很小,忽略不计. )方法区 这个名字很让我迷惑. ...

  10. java中内存的使用

    一个java运行起来执行代码,主要的内存消耗有这几块: 1.堆 2.栈 :栈是每个线程一个的,是以消耗的内存是内存大小*线程数,当线程数特多时候需要小心 . 3.直接内存:主要是通道时候的缓存,在内存 ...