最近在寫Driver時,常常遇到需要「等待一段時間」再處理的動作,以往我都傻傻的用msleep()或mdelay(),殊不知這種busy waiting會hold住cpu資源,在這段期間內都無法讓給其他process執行,時間短(10ms以下等級)或許還可以,太長就不行了,所以需要Kernel本身就有提供的「時序」機制來做處理,於是我漸漸學會了如何使用Timer、Tasklet和Workqueue的用法,在O'Reilly的Linux Device Drivers第七章有詳細的講解,我將書上的精華茲簡單整理如下:
##CONTINUE##
一、Timer
#include <linux/timer.h>
struct timer_list
{
/* ... */
unsigned long expires;//期望運行的 jiffies 值
void (*function)(unsigned long);
unsigned long data;//傳給function的參數
};//使用前必須初始化
void init_timer(struct timer_list *timer);
//初始化一個timer_list
struct timer_list
TIMER_INITIALIZER(_function, _expires, _data);
//初始化一個靜態的timer_list
//初始化一個靜態的timer_list
void
add_timer(struct timer_list * timer);
int mod_timer(struct timer_list *timer, unsigned long expires);
int del_timer(struct timer_list * timer);
int del_timer_sync(struct timer_list *timer);
//同 del_timer 一樣,但保證函數return時,定時器函數不在任何 CPU 上運行
//避免在SMP 系統上競爭
int mod_timer(struct timer_list *timer, unsigned long expires);
int del_timer(struct timer_list * timer);
int del_timer_sync(struct timer_list *timer);
//同 del_timer 一樣,但保證函數return時,定時器函數不在任何 CPU 上運行
//避免在SMP 系統上競爭
int
timer_pending(const struct timer_list * timer);
//返回真或假來指示是否定時器已被運行
二、Tasklet
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data);
//將定義好的結構體初始化,才可用
DECLARE_TASKLET(name, func, data);
//直接申明就可用了
DECLARE_TASKLET_DISABLED(name, func, data);
void tasklet_disable(struct tasklet_struct *t);
void tasklet_disable_nosync(struct tasklet_struct *t);
//對正在運行的tasklet無效,當它返回時,這個 tasklt 被禁止並且不會在以後被調度,直到重新enable.
void tasklet_enable(struct tasklet_struct *t);
//如果這個 tasklet 已經被調度, 它會很快運行. 一個對 tasklet_enable 的調用必須匹配每個對tasklet_disable 的調用, 因為內核跟蹤每個 tasklet 的"禁止次數".
void tasklet_schedule(struct tasklet_struct *t);
//調度 tasklet 執行.一般只運行一次,但可在一個tasklet中再調用,到達多次運行的目的
void tasklet_hi_schedule(struct tasklet_struct *t);
//調度 tasklet 在更高優先級執行.高於一般的tasklet優先級
void tasklet_kill(struct tasklet_struct *t);
//當一個設備正被關閉或者模塊卸載時,被調用。若tasklet被調度,則從運行的list中去掉 tasklet.若tasklet在另一個 CPU 上運行,則阻塞等待tasklet終止.
注意:tasklet是不可中斷的,一個tasklet只在一個CPU上運行
三、Workqueue
非共享隊列
1.創建工作隊列
struct workqueue_struct * create_workqueue(const char *name);
struct workqueue_struct * create_singlethread_workqueue(const char *name);
2.創建工作
DECLARE_WORK(name, void (*function)(void *), void *data);
//一個函數搞定定義和初始化
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
//要先定義work_struct,再調用這兩個函數
//INIT_WORK做更全的初始化,若需要改變工作隊列,則用PREPARE_WORK
3.提交工作給一個工作隊列
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);
//返回0,添加成功,非0表明已存在此工作
int cancel_delayed_work(struct work_struct *work);
//返回非0,取消成功,返回0,取消不成功,工作仍在運行
void flush_workqueue(struct workqueue_struct *queue);
//確保cancel_delayed_work調用返回0後,停止運行工作
//返回真或假來指示是否定時器已被運行
二、Tasklet
#include <linux/interrupt.h>
struct tasklet_struct {
/* ... */
void (*func)(unsigned long);
unsigned long data;
};
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data);
//將定義好的結構體初始化,才可用
DECLARE_TASKLET(name, func, data);
//直接申明就可用了
DECLARE_TASKLET_DISABLED(name, func, data);
void tasklet_disable(struct tasklet_struct *t);
void tasklet_disable_nosync(struct tasklet_struct *t);
//對正在運行的tasklet無效,當它返回時,這個 tasklt 被禁止並且不會在以後被調度,直到重新enable.
void tasklet_enable(struct tasklet_struct *t);
//如果這個 tasklet 已經被調度, 它會很快運行. 一個對 tasklet_enable 的調用必須匹配每個對tasklet_disable 的調用, 因為內核跟蹤每個 tasklet 的"禁止次數".
void tasklet_schedule(struct tasklet_struct *t);
//調度 tasklet 執行.一般只運行一次,但可在一個tasklet中再調用,到達多次運行的目的
void tasklet_hi_schedule(struct tasklet_struct *t);
//調度 tasklet 在更高優先級執行.高於一般的tasklet優先級
void tasklet_kill(struct tasklet_struct *t);
//當一個設備正被關閉或者模塊卸載時,被調用。若tasklet被調度,則從運行的list中去掉 tasklet.若tasklet在另一個 CPU 上運行,則阻塞等待tasklet終止.
注意:tasklet是不可中斷的,一個tasklet只在一個CPU上運行
三、Workqueue
非共享隊列
#include <linux/workqueue.h>
struct workqueue_struct {
unsigned long pending;
struct list_head entry;
void (*func)(void *);
void *data;
void *wq_data;
struct timer_list timer;
};
1.創建工作隊列
struct workqueue_struct * create_workqueue(const char *name);
struct workqueue_struct * create_singlethread_workqueue(const char *name);
2.創建工作
DECLARE_WORK(name, void (*function)(void *), void *data);
//一個函數搞定定義和初始化
INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);
PREPARE_WORK(struct work_struct *work, void (*function)(void *), void *data);
//要先定義work_struct,再調用這兩個函數
//INIT_WORK做更全的初始化,若需要改變工作隊列,則用PREPARE_WORK
3.提交工作給一個工作隊列
int queue_work(struct workqueue_struct *queue, struct work_struct *work);
int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);
//返回0,添加成功,非0表明已存在此工作
int cancel_delayed_work(struct work_struct *work);
//返回非0,取消成功,返回0,取消不成功,工作仍在運行
void flush_workqueue(struct workqueue_struct *queue);
//確保cancel_delayed_work調用返回0後,停止運行工作
4.刪除隊列
void
destroy_workqueue(struct workqueue_struct *queue);
//用完後刪掉工作隊列
共享隊列
1.創建工作
同非共享
//用完後刪掉工作隊列
共享隊列
1.創建工作
同非共享
2.提交工作給一個工作隊列
int schedule_work(struct work_struct *work);
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
void flush_scheduled_work(void);
//刷新 同flush_workqueue一樣
int schedule_work(struct work_struct *work);
int schedule_delayed_work(struct work_struct *work, unsigned long delay);
void flush_scheduled_work(void);
//刷新 同flush_workqueue一樣
注意:工作隊列可休眠