對於內核中常用的中斷處理機制做一些總結,方便在合適的時候採用合適的機制。
tasklet 和 work_queue 是延期執行工作的機制,實現基於軟中斷;completion的實現基於wait_queue。
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
tasklet
小進程,主要用於執行一些小任務,對這些任務使用全功能進程比較浪費。也稱爲中斷下半部,在處理軟中斷時執行。
definition:
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
相關定義:
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
enum
{
TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */
TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
};
初始化及銷燬tasklet的方法:
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
void tasklet_kill(struct tasklet_struct *t)
{
if (in_interrupt())
printk("Attempt to kill tasklet from interrupt\n");
while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
do {
yield();
} while (test_bit(TASKLET_STATE_SCHED, &t->state));
}
tasklet_unlock_wait(t);
clear_bit(TASKLET_STATE_SCHED, &t->state);
}
調度tasklet的方法:
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = NULL;
*__this_cpu_read(tasklet_vec.tail) = t;
__this_cpu_write(tasklet_vec.tail, &(t->next));
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
使用方法:
tasklet_init->tasklet_schedule
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
wait_queue
用於使進程等待某一事件的發生,無須頻繁輪訊,進程在等待期間睡眠,在事件發生時由內核自動喚醒。
用法1(add_wait_queue 和 wake_up組合):
當nand控制器被一個進程使用時,其餘進程被放入等待隊列;待第一個進程使用結束後,調用wake_up喚醒等待隊列,下一個進程獲得nand控制器的使用權。
#define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.task_list = { NULL, NULL } }
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
<nand_base.c>
static int nand_test_get_device(struct mtd_info *mtd, int new_state)
{
struct nand_chip *chip = mtd->priv;
spinlock_t *lock = &chip->controller->lock;
wait_queue_head_t *wq = &chip->controller->wq;
DECLARE_WAITQUEUE(wait, current);
retry:
spin_lock(lock);
/* Hardware controller shared among independent devices */
if (!chip->controller->active)
chip->controller->active = chip;
if (chip->controller->active == chip && chip->state == FL_READY) {
chip->state = new_state;
spin_unlock(lock);
return 0;
}
if (new_state == FL_PM_SUSPENDED) {
if (chip->controller->active->state == FL_PM_SUSPENDED) {
chip->state = FL_PM_SUSPENDED;
spin_unlock(lock);
return 0;
}
}
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(wq, &wait);
spin_unlock(lock);
schedule();
remove_wait_queue(wq, &wait);
goto retry;
}
static void nand_test_release_device(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
/* Release the controller and the chip */
spin_lock(&chip->controller->lock);
chip->controller->active = NULL;
chip->state = FL_READY;
wake_up(&chip->controller->wq);
spin_unlock(&chip->controller->lock);
}
方法2(prepare_to_wait 和 wake_up組合),可作爲實現阻塞的方式:
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \
.private = current, \
.func = function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
autoremove_wake_function會調用default_wake_function,然後將所屬等待隊列成員從等待隊列中刪除。
void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
static long do_CustomEvent_wait(long eventNum)
{
struct CustomEvent *tmp = NULL;
struct CustomEvent *prev = NULL;
pr_info("Enten into %s, the eventNum is %d\n", __func__, eventNum);
if((tmp = FindEventNum(eventNum, &prev)) != NULL) {
DEFINE_WAIT(wait);
prepare_to_wait(tmp->p, &wait, TASK_INTERRUPTIBLE);
schedule();
finish_wait(tmp->p, &wait);
return eventNum;
}
return -1;
}
static long do_CustomEvent_signal(long eventNum)
{
struct CustomEvent *tmp = NULL;
struct CustomEvent *prev = NULL;
pr_info("Enten into %s, the eventNum is %d\n", __func__, eventNum);
if(!(tmp = FindEventNum(eventNum, &prev)))
return 0;
wake_up(tmp->p);
return 1;
}
第三種方法(wait_event 和 wake_up組合):
該方法首先將進程的狀態設爲 TASK_UNINTERRUPTIBLE, 之後判斷條件是否滿足;條件不滿足時,通過schedule()將該進程從runqueue隊列中移除,該進程進入睡眠狀態;只有當某進程調用wake_up才能再次喚醒該進程;進程被喚醒後,再次將該進程的狀態置爲TASK_UNINTERRUPTIBLE,然後判斷條件是否滿足。條件滿足時結束循環,由finish_wait將進程的狀態設置爲TASK_RUNNING,並從等待隊列的鏈表中移除相應項。
#define __wait_event(wq, condition) \
do { \
DEFINE_WAIT(__wait); \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
if (condition) \
break; \
schedule(); \
} \
finish_wait(&wq, &__wait); \
} while (0)
#define wait_event(wq, condition) \
do { \
if (condition) \
break; \
__wait_event(wq, condition); \
} while (0)
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
completion:
基於等待隊列,內核利用該機制等待某一操作結束。由於和wait_queue用法類似,不再詳細描述。
completion的操作接口:
init_completion()封裝了init_waitqueue_head()接口;
wait_for_completion()封裝了wait_for_common()接口;
complete()封裝了__wake_up_common()接口。
——————————————————————————————————————————————————————————————————————————————————————————————————————————————
work_queue:
由於在中斷中不能進行阻塞型操作,而有時候需要在中斷時讀取某些內存單元或寄存器的值,此時可以考慮利用工作隊列來實現。
工作隊列的定義:
工作隊列是將操作延期的一種手段。因爲他們是通過守護進程在用戶上下文執行,函數可以睡眠任意長的時間,這與內核無關。替換了之前的kevented機制。
工作隊列的使用:
使用工作隊列,主要有以下三個步驟:
1 實現工作任務處理函數
2 創建並初始化工作任務
3 將工作任務添加到某工作隊列中,等待系統調度
<kernel/workqueue.c>
int schedule_work(struct work_struct *work)
int schedule_delayed_work(struct work_struct *dwork, unsigned long delay)
1 定義工作任務處理函數:
static void func(struct work_struct *work)
{
......
}
2 創建並初始化工作任務
可採用兩種方式創建並初始化工作任務:
1) 先創建工作任務,後綁定處理函數:
struct work_struct xxx_wq;//創建工作任務
在模塊初始化的時候:
INIT_WORK(&xxx_wq,func);//初始化工作任務,工作任務需要執行的是函數func
2) 創建工作任務的同時初始化:
DECLARE_WORK(xxx_wq, func);
3 在中斷處理函數中將工作任務添加到工作隊列,等待系統調度
static inline bool queue_work(struct workqueue_struct *wq, struct work_struct *work)
{
return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
用於將某工作任務添加到某工作隊列
內核創建了一個標準的工作隊列events,內核的各個部分若無必要創建獨立的工作隊列,可直接使用該工作隊列。
static inline bool schedule_work(struct work_struct *work)//將工作任務xxx_wq添加到標準工作隊列events中,中斷處理完成之後可以立即執行func
{
return queue_work(system_wq, work);
}