Linux驅動技術(七) _內核定時器與延遲工做

內核定時器

軟件上的定時器最終要依靠硬件時鐘來實現,簡單的說,內核會在時鐘中斷髮生後檢測各個註冊到內核的定時器是否到期,若是到期,就回調相應的註冊函數,將其做爲中斷底半部來執行。實際上,時鐘中斷處理程序會觸發TIMER_SOFTIRQ軟中斷,運行當前處理器上到期的全部定時器。
設備驅動程序如要得到時間信息以及須要定時服務,均可以使用內核定時器。linux

jiffies

要說內核定時器,首先就得說說內核中關於時間的一個重要的概念:jiffies變量,做爲內核時鐘的基礎,jiffies每隔一個固定的時間就會增長1,稱爲增長一個節拍,這個固定間隔由定時器中斷來實現,每秒中產生多少個定時器中斷,由在<linux/param.h>中定義的HZ宏來肯定,如此,能夠經過jiffies獲取一段時間,好比jiffies/HZ表示自系統啓動的秒數。下兩秒就是(jiffies/HZ+2),內核中用jiffies來計時,秒轉換成的jiffies:seconds*HZ,因此以jiffiy爲單位,以當前時刻爲基準計時2秒:(jiffies/HZ+2)*HZ=jiffies+2*HZ若是要獲取當前時間,可使用do_gettimeofday(),該函數填充一個struct timeval結構,有着接近微妙的分辨率。函數

//kernel/time/timekeeping.c
 473 /**
 474  * do_gettimeofday - Returns the time of day in a timeval
 475  * @tv:         pointer to the timeval to be set
 476  *
 477  * NOTE: Users should be converted to using getnstimeofday()
 478  */
 479 void do_gettimeofday(struct timeval *tv)

驅動程序爲了讓硬件有足夠的時間完成一些任務,經常須要將特定的代碼延後一段時間來執行,根據延時的長短,內核開發中使用長延時短延時兩個概念。長延時的定義爲:延時時間>多個jiffies,實現長延時能夠用查詢jiffies的方法:atom

time_before(jiffies, new_jiffies);
time_after(new_jiffiesmjiffies);

**短延時的定義爲:延遲事件接近或短於一個jiffy,實現短延時能夠調用code

udelay();
mdelay();

這兩個函數都是忙等待函數,大量消耗CPU時間,前者使用軟件循環來延遲指定數目的微妙數,後者使用前者的嵌套來實現毫秒級的延時。對象

定時器

驅動能夠註冊一個內核定時器,來指定一個函數在將來某個時間來執行。定時器從註冊到內核開始計時,達到指定的時間後會執行註冊的函數。即超時值是一個jiffies值,當jiffies值大於timer->expires時,timer->function就會被執行。API以下隊列

//定一個定時器
struct timer_list my_timer;

//初始化定時器
void init_timer(struct timer_list *timer);
mytimer.function = my_function;
mytimer.expires = jiffies +HZ;

//增長定時器
void add_timer(struct timer_list *timer);

//刪除定時器
int del_tiemr(struct timer_list *timer);

實例

static struct timer_list tm;
struct timeval oldtv;

void callback(unsigned long arg)
{
    struct timeval tv;
    char *strp = (char*)arg;
    do_gettimeofday(&tv);
    printk("%s: %ld, %ld\n", __func__,
        tv.tv_sec - oldtv.tv_sec,
        tv.tv_usec- oldtv.tv_usec);
    oldtv = tv;
    tm.expires = jiffies+1*HZ;
    add_timer(&tm);
}

static int __init demo_init(void)
{
    init_timer(&tm);
    do_gettimeofday(&oldtv);
    tm.function= callback;
    tm.data    = (unsigned long)"hello world";
    tm.expires = jiffies+1*HZ;
    add_timer(&tm);
    return 0;
}

延遲工做

除了使用內核定時器完成定時延遲工做,Linux內核還提供了一套封裝好的"快捷方式"-delayed_work,和內核定時器相似,其本質也是利用工做隊列和定時器實現,事件

//include/linux/workqueue.h
100 struct work_struct {           
101         atomic_long_t data;
102         struct list_head entry;
103         work_func_t func;
104 #ifdef CONFIG_LOCKDEP
105         struct lockdep_map lockdep_map;
106 #endif
107 };
113 struct delayed_work {              
114         struct work_struct work;
115         struct timer_list timer;
116 
117         /* target workqueue and CPU ->timer uses to queue ->work */
118         struct workqueue_struct *wq;
119         int cpu;
120 };

struct work_struct
--103-->須要延遲執行的函數, typedef void (work_func_t)(struct work_struct work);開發

至此,咱們可使用一個delayed_work對象以及相應的調度API實現對指定任務的延時執行get

//註冊一個延遲執行
591 static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
//註銷一個延遲執行
2975 bool cancel_delayed_work(struct delayed_work *dwork)

和內核定時器同樣,延遲執行只會在超時的時候執行一次,若是要實現循環延遲,只須要在註冊的函數中再次註冊一個延遲執行函數。it

schedule_delayed_work(&work,msecs_to_jiffies(poll_interval));
相關文章
相關標籤/搜索