天天看點

核心定時器

 在核心中,如果我們想在将來的某個時間點排程執行某個動作,同時在該時間點到達之前不會阻塞目前程序,那麼核心定時器是個不錯的選擇。例如,一些周期性的操作等等。

那麼到底什麼是定時器呢,要實作一個定時器,都需要哪些操作?

核心定時器是一個資料結構,它告訴核心在一個使用者定義的時間點,使用使用者定義的參數來執行一個使用者定義的函數。其資料結構是這樣的:

#include 

struct timer_list {

    struct list_head entry;//核心使用

    unsigned long expires;//逾時的jiffies值

    void (*function)(unsigned long);//逾時處理函數

    unsigned long data;//逾時處理函數參數

    struct tvec_t_base_s *base;//核心使用

#ifdef CONFIG_TIMER_STATS

    void *start_site;

    char start_comm[16];

    int start_pid;

#endif

};

其中,expires是表示定時器執行是的時間值(即jiffies值);在到達該jiffies時間的時候,函數function将被調用,并傳遞data參數。

當然,如果要傳遞的參數不止一個的時候,可以将參數捆綁成一個資料結構,然後将該資料結構的指針強制轉換成unsigned long傳入。

定時器timer的基本操作也比較直覺。可能讀者已經猜到一二了,初始化、加入到定時器連結清單、在必要的時候(比如解除安裝子產品)delet掉就行了。

1. 初始化時應該首先調用init_timer初始化除expires、function、data之外的參數,然後再調用宏TIMER_INITIALIZER來初始化這三個參數:

void fastcall init_timer(struct timer_list *timer)

{

    timer->entry.next = NULL;

    timer->base = __raw_get_cpu_var(tvec_bases);

#ifdef CONFIG_TIMER_STATS

    timer->start_site = NULL;

    timer->start_pid = -1;

    memset(timer->start_comm, 0, TASK_COMM_LEN);

#endif

}

#define TIMER_INITIALIZER(_function, _expires, _data) {        \

        .function = (_function),            \

        .expires = (_expires),                \

        .data = (_data),                \

        .base = &boot_tvec_bases,            \

    }

另外一個宏DEFINE_TIMER也可以用來初始化

#define DEFINE_TIMER(_name, _function, _expires, _data)        \

    struct timer_list _name =                \

        TIMER_INITIALIZER(_function, _expires, _data)

2. 加載定時器

extern void add_timer(struct timer_list *timer);

3. 删除定時器

extern int del_timer(struct timer_list * timer);

當然,針對定時器還有一些其他的函數:

extern int mod_timer(struct timer_list *timer, unsigned long expires);更新一個定時器的到期時間

extern void add_timer_on(struct timer_list *timer, int cpu);将定時器加載到指定的cpu上,用于對稱多處理器(smp)

static inline int timer_pending(const struct timer_list * timer)傳回定時器是否正在被排程運作

還有很多函數,具體參見linux/timer.h

下面給出一個執行個體,在2.6.24核心測試通過:

#include 

#include 

#include 

#include 

#include 

static void timer_handler (unsigned long data)

{

    printk(KERN_ALERT "The jiffies in timer_handler is %lu\n",jiffies);

        printk(KERN_ALERT "timer_handler is running.\n");

}

DEFINE_TIMER(my_timer,timer_handler,HZ,0);

static int __init test_init(void)

{

    printk(KERN_ALERT "The jiffies is %lu\n",jiffies);

    add_timer(&my_timer);

        return 0;

}

static  void __exit test_exit(void)

{

    del_timer(&my_timer);

        printk(KERN_ALERT "test_exit is running.\n");

}

MODULE_LICENSE("GPL");

module_init(test_init);

module_exit(test_exit);

測試結果:

The jiffies is 2425862

The jiffies in timer_handler is 2425863

timer_handler is running.

test_exit is running.

最後,還有幾點關于核心定時器的說明:

  • 核心定時器通常作為“軟中斷”在中斷上下文執行,是以不能通路使用者空間、此時current指針沒有任何意義、不能排程和休眠。可用in_interrupt()來判斷是否正在運作在中斷上下文。
  • 在SMP系統中,定時器函數會由注冊它的同一CPU執行。
  • 核心定時器也可以将自己注冊以在稍後的時間重新運作。
  • 定時器也可能是競态的來源,也就是說定時器也可能導緻并發與競争。

其實中斷中的tasklet機制很類似于核心定時器。

關于tasklet和工作隊列,參見

http://www.kerneltravel.net/?p=143

http://www.kerneltravel.net/?p=257

繼續閱讀