内核定时器 timer_list 分析

来源:互联网 发布:知乎数据挖掘 编辑:程序博客网 时间:2024/04/28 19:46

struct timer_list
|-----------------------------------|
|struct list_head     entry         |
|unsigned long        expires       |
|void     (*function)(unsigned long)|
|unsigned long        data          |
|struct tvec_t_base_s *base         |--+
|-----------------------------------| -|
|void                 *start_site   | 
-| CONFIG_TIMER_STATS
|char                 start_comm[16]| 
-|
|int                  start_pid     | 
-|
|-----------------------------------| 
-|
                                       |
+--------------------------------------+
|   时钟向量base结构tvec_t_base_s
|         struct tvec_t_base_s
+-->|---------------------------------|
    |spinlock_t        lock           |
    |struct timer_list *running_timer |
    |unsigned long     timer_jiffies 
-| base的基准时间
    |tvec_root_t       tv1            | root hash链表
    |tvec_t            tv2            | 二级hash链表
    |tvec_t            tv3            | 三级hash链表
    |tvec_t            tv4            | 四级hash链表
    |tvec_t            tv5            | 五级hash链表
    |---------------------------------|
       ____cacheline_aligned_in_smp


expires    定时器定时的滴答数(当前的滴答数为jiffies)
function   到那个时刻内核调用的函数
data       由于可能多个定时器调用一个函数,为了使得这个函数能够区分不同的定时器,通过在结构中data来标识这个定时器,并且通过调用function(data)使得function能区分它们,也就是 data起到ID的作用。


    使用时钟,先声明一个timer_list结构,调用init_timer对它进行初始化。 time_list结构里expires是标明这个时钟的周期,单位采用jiffies的单位。function就是时间到了以后的回调函数,它的参数就是timer_list中的data。 data这个参数在初始化时钟的时候赋值,一般赋给它设备的device结构指针。在预置时间到系统调用function,同时系统把这个time_list从定时队列里清除。所以如果需要一直使用定时函数,要在function里再次调用add_timer()把这个timer_list加进定时队列。


管理定时器的接口
----------------------------------------------------------
    创建定时器需要先定义它:
    struct timer_list my_timer;
    接着需要通过一个辅助函数初始化定时器数据结构的内部值,初始化必须在使用其他定时器管理函数对定时器进行操作前完成。
    init_timer(&my_timer);
    现在可以填充结构中需要的值了(与定时器直接相关的三个参数):
    my_timer.expires = jiffies + delay;   // 定时器超时时的节拍数
    my_timer.data     = 0;                // 给定时器处理函数传入参数0
    my_timer.function = my_function;      // 定时器超时时调用的函数
    my_timer.expires表示超时时间,它是以节拍为单位的绝对计数值。如果当前jiffies计数等于或大于my_timer.expires,那么my_function指向的处理函数就会开始执行,另外该函数还要使用长整型参数my_timer.data。如果你不需要这个参数,可以简单地传递0(或任何其他值)给处理函数。

    最后,必须激活定时器:
    add_timer(&my_timer);
    大功告成,定时器可以工作了!但请注意定时值的重要性。当前节拍计数大于等于指定的超时值时,内核就开始执行定时器处理函数。虽然内核可以保证不会在超时时间到期前运行定时器处理函数,但是有可能延误定时器的执行。一般来说,定时器都在超时后马上就回执行,但是也有可能被推迟到一下时钟节拍才能运行,所以不能用定时器来实现任何硬实时任务。

    有时可能需要更改已经激活的定时器超时时间,所以内核通过函数mod_timer()来实现该功能,该函数可以改变指定的定时器超时时间:
    mod_timer(&my_timer, jiffies+new_delay);
    mod_timer()函数也可操作那些已经初始化,但还没有被激活的定时器,如果定时器未被激活,mod_timer()会激活它。如果调用时定时器未被激活,该函数返回0;否则返回1。但不论哪种情况,一旦从mod_timer()函数返回,定时器都将被激活而且设置了新的定时值。

    如果需要在定时器超时前停止定时器,可以使用del_timer()函数:
    del_timer(&my_timer);
    被激活或未被激活的定时器都可以使用该函数,如果定时器还未被激活,该函数返回0;否则返回1。注意,你不需要为已经超时的定时器调用该函数,因为它们超时后会自动被删除。


    当删除定时器时,必须小心一个潜在的竞争条件。当del_timer()返回后,可以保真的只是: 定时器不会再被激活(也就是,将来不会执行),但是在多处理机器上定时器中断可能已经在其他处理器上运行了,所以删除定时器时需要等待可能在其他处理器上运行的定时器处理程序都退出,这时就要使用del_timer_sync()函数执行删除工作:
    del_timer_sync(&my_timer);
    和del_timer()函数不同,del_timer_sync()函数不能在中断上下文中使用。




tvec_t, tvec_root_t
 hash链表 -- 
tvec_t_base_s中的hash链表
-------------------------------------------------------
--typedef struct tvec_s
|------------------------|
|struct list_head vec[0] 
|
|------------------------|
|                        |
|------------------------|
|                        |
|------------------------|
|                        |
|------------------------|
|                        |
|------------------------|
|struct list_head vec[31]|
|------------------------|
         tvec_t

typedef struct tvec_root_s
|-------------------------|
|struct list_head vec[0] 
-|
|-------------------------|
|                         |
|-------------------------|
|                         |
|-------------------------|
|                         |
|-------------------------|
|                         |
|-------------------------|
|struct list_head vec[128]|
|-------------------------|
    tvec_root_t


TVN_SIZE = 32 (or 128)
TVR_SIZE = 128 (or 512)

-------------------------------------------------------
根据内核是否配置了CONFIG_BASE_SMALL来确定向量链表数是32还是128
#define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)
根据内核是否配置了CONFIG_BASE_SMALL来确定root链表数是128还是512
#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)



boot_tvec_bases
-------------------------------------------------------
定义内核时钟base全局变量
typedef struct tvec_t_base_s tvec_base_t;
tvec_base_t boot_tvec_bases;
EXPORT_SYMBOL(boot_tvec_bases);

为每个CPU定义一个时钟向量基表
static DEFINE_PER_CPU(tvec_base_t *, tvec_bases) = { &boot_tvec_bases };



就位后的timer和tvec_t_bash_s的关系视图
----------------------------------------------------------
tvec_t_base_s
|----------|
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |       timer_list        
timer_list        timer_list
|----------|      |----------|      |----------|      |----------|
-vec[i] -|<---->|entry     |<---->|entry     |<---->|entry     |
|----------|      |expires   |      |expires   |      |expires   |
|          |      |function()|      |function()|      |function()|
|          |      |data      |      |data      |      |data      |
|          |   +--|*base     |   +--|*base     |   +--|*base     |
|          |   | -|----------|   | 
-|----------|   | -|----------|
|          |   |                 |                 |
|          |   |                 |                 |
|----------|<--+-----------------+-----------------+

原创粉丝点击