Linux核心定時器與延遲工作是兩種常用的實現定時任務和延遲執行任務的機制,它們可以讓驅動程式在適當的時間點執行特定的函數,以適應硬體設備的需求和特性。但是,如何正確地使用Linux內核定時器與延遲工作呢?本文將從理論與實務兩方面,介紹Linux核心定時器與延遲工作驅動發展的基本知識與技巧,以及一些常見的問題與解決方法。
#軟體上的定時器最終要依靠硬體時鐘來實現,簡單的說,內核會在時鐘中斷發生後檢測各個註冊到內核的定時器是否到期,如果到期,就回調相應的註冊函數,將其作為中斷底半部來執行。實際上,時脈中斷處理程序會觸發TIMER_SOFTIRQ軟中斷,運行目前處理器上到期的所有計時器。
設備驅動程式如要取得時間資訊以及需要定時服務,都可以使用核心定時器。
#要說內核定時器,首先就得說說內核中關於時間的一個重要的概念:jiffies變數,作為內核時脈的基礎,jiffies每隔一個固定的時間就會增加1 ,稱為增加一個節拍,這個固定間隔由定時器中斷來實現,每秒中產生多少個定時器中斷,由在
//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的方法:
time_before(jiffies, new_jiffies); time_after(new_jiffiesmjiffies);
**短延時的定義為:延遲事件接近或短於一個jiffy,實作短延時可以呼叫
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实现对指定任务的延时执行
//注册一个延迟执行 591 static inline bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay) //注销一个延迟执行 2975 bool cancel_delayed_work(struct delayed_work *dwork)
和内核定时器一样,延迟执行只会在超时的时候执行一次,如果要实现循环延迟,只需要在注册的函数中再次注册一个延迟执行函数。
schedule_delayed_work(&work,msecs_to_jiffies(poll_interval));
本文从理论和实践两方面,详细介绍了Linux内核定时器与延迟工作驱动开发的基本知识和技巧。我们首先了解了Linux内核定时器与延迟工作的概念、原理、特点和API函数,然后学习了如何使用Linux内核定时器与延迟工作来实现按键事件的检测和处理。最后,我们介绍了一些在Linux内核定时器与延迟工作驱动开发过程中可能遇到的问题,以及相应的解决方法。
通过本文,我们希望能够帮助你掌握Linux内核定时器与延迟工作驱动开发的基本方法和技巧,为你在嵌入式Linux领域的进一步学习和工作打下坚实的基础。
以上是Linux核心定時器與延遲工作驅動開發詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!