等待隊列
定義:
include/linux/wait.h struct __wait_queue_head { spinlock_t lock; struct list_head task_list; }; typedef struct __wait_queue_head wait_queue_head_t;
二、作用:
在內核裏麵,等待隊列是有很多用處的,尤其是在中斷處理、進程同步、定時等場合。可以使用等待隊列在實現阻塞進程的喚醒。它以隊列為基礎數據結構,與進程調度機製緊密結合,能夠用於實現內核中的異步事件通知機製,同步對係統資源的訪問等。
三、字段詳解:
1、spinlock_t lock;
在對task_list與操作的過程中,使用該鎖實現對等待隊列的互斥訪問。
2、srtuct list_head_t task_list;
雙向循環鏈表,存放等待的進程。
三、操作:
1、定義並初始化:
(1)
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
直接定義並初始化。init_waitqueue_head()函數會將自旋鎖初始化為未鎖,等待隊列初始化為空的雙向循環鏈表。
(2)
DECLARE_WAIT_QUEUE_HEAD(my_queue);
定義並初始化,相當於(1)。
(3)定義等待隊列:
DECLARE_WAITQUEUE(name,tsk);
注意此處是定義一個wait_queue_t類型的變量name,並將其private與設置為tsk。wait_queue_t類型定義如下:struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
其中flags域指明該等待的進程是互斥進程還是非互斥進程。 其中0是非互斥進程,WQ_FLAG_EXCLUSIVE(0x01)是互斥進程。等待隊列(wait_queue_t)和等待對列頭 (wait_queue_head_t)的區別是等待隊列是等待隊列頭的成員。也就是說等待隊列頭的task_list域鏈接的成員就是等待隊列類型的 (wait_queue_t)。
2、(從等待隊列頭中)添加/移出等待隊列:
(1)add_wait_queue()函數:
void fastcall 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); }
設置等待的進程為非互斥進程,並將其添加進等待隊列頭(q)的隊頭中。
void fastcall add_wait_queue_exclusive(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_tail(q, wait); spin_unlock_irqrestore(&q->lock, flags); }
該函數也和add_wait_queue()函數功能基本一樣,隻不過它是將等待的進程(wait)設置為互斥進程。
(2)remove_wait_queue()函數:
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; spin_lock_irqsave(&q->lock, flags); __remove_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock, flags); }
在等待的資源或事件滿足時,進程被喚醒,使用該函數被從等待頭中刪除。
3、等待事件:
(1)wait_event()宏:
#define wait_event(wq, condition) / do { / if (condition) / break; / __wait_event(wq, condition); / } while (0) #define __wait_event_timeout(wq, condition, ret) / do { / DEFINE_WAIT(__wait); / / for (;;) { / prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); / if (condition) / break; / ret = schedule_timeout(ret); / if (!ret) / break; / } / finish_wait(&wq, &__wait); / } while (0)
在等待會列中睡眠直到condition為真。在等待的期間,進程會被置為TASK_UNINTERRUPTIBLE進入睡眠,直到condition變量變為真。每次進程被喚醒的時候都會檢查condition的值.
(2)wait_event_interruptible()函數:
和wait_event()的區別是調用該宏在等待的過程中當前進程會被設置為TASK_INTERRUPTIBLE狀態.在每次被喚醒的時候,首先檢查condition是否為真,如果為真則返回,否則檢查如果進程是被信號喚醒,會返回-ERESTARTSYS錯誤碼.如果是condition為真,則返回0.
(3)wait_event_timeout()宏:
也與wait_event()類似.不過如果所給的睡眠時間為負數則立即返回.如果在睡眠期間被喚醒,且condition為真則返回剩餘的睡眠時間,否則繼續睡眠直到到達或超過給定的睡眠時間,然後返回0.
(4)wait_event_interruptible_timeout()宏:
與wait_event_timeout()類似,不過如果在睡眠期間被信號打斷則返回ERESTARTSYS錯誤碼.
(5) wait_event_interruptible_exclusive()宏
同樣和wait_event_interruptible()一樣,不過該睡眠的進程是一個互斥進程.
5、喚醒隊列:
(1)wake_up()函數:
#define wake_up(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL) void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, void *key) { unsigned long flags; spin_lock_irqsave(&q->lock, flags); __wake_up_common(q, mode, nr_exclusive, 0, key); spin_unlock_irqrestore(&q->lock, flags); } static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int sync, void *key) { struct list_head *tmp, *next; list_for_each_safe(tmp, next, &q->task_list) { wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list); unsigned flags = curr->flags; if (curr->func(curr, mode, sync, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; } }
喚醒等待隊列.可喚醒處於TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE狀態的進程,和wait_event/wait_event_timeout成對使用.
(2)wake_up_interruptible()函數:
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
和wake_up()唯一的區別是它隻能喚醒TASK_INTERRUPTIBLE狀態的進程.,與wait_event_interruptible/wait_event_interruptible_timeout/ wait_event_interruptible_exclusive成對使用.
(3)
#define wake_up_all(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL) #define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL) #define wake_up_interruptible_all(x) __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
這些也基本都和wake_up/wake_up_interruptible一樣.
6、在等待隊列上睡眠:
(1)sleep_on()函數:
void __sched sleep_on(wait_queue_head_t *q) { unsigned long flags; wait_queue_t wait; init_waitqueue_entry(&wait, current); current->state = TASK_UNINTERRUPTIBLE; sleep_on_head(q, &wait, &flags); schedule(); sleep_on_tail(q, &wait, &flags); }
該函數的作用是定義一個等待隊列(wait),並將當前進程添加到等待隊列中(wait),然後將當前進程的狀態置為TASK_UNINTERRUPTIBLE,並將等待隊列(wait)添加到等待隊列頭(q)中。之後就被掛起直到資源可以獲取,才被從等待隊列頭(q)中喚醒,從等待隊列頭中移出。在被掛起等待資源期間,該進程不能被信號喚醒。
(2)sleep_on_timeout()函數:
long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout) { unsigned long flags; wait_queue_t wait init_waitqueue_entry(&wait, current); current->state = TASK_UNINTERRUPTIBLE; sleep_on_head(q, &wait, &flags); timeout = schedule_timeout(timeout); sleep_on_tail(q, &wait, &flags); return timeout; }
與sleep_on()函數的區別在於調用該函數時,如果在指定的時間內(timeout)沒有獲得等待的資源就會返回。實際上是調用schedule_timeout()函數實現的。值得注意的是如果所給的睡眠時間(timeout)小於0,則不會睡眠。該函數返回的是真正的睡眠時間。
(3)interruptible_sleep_on()函數:
void __sched interruptible_sleep_on(wait_queue_head_t *q) { unsigned long flags; wait_queue_t wait; init_waitqueue_entry(&wait, current); current->state = TASK_INTERRUPTIBLE; sleep_on_head(q, &wait, &flags); schedule(); sleep_on_tail(q, &wait, &flags); }
該函數和sleep_on()函數唯一的區別是將當前進程的狀態置為TASK_INTERRUPTINLE,這意味在睡眠如果該進程收到信號則會被喚醒。
(4)interruptible_sleep_on_timeout()函數:
long __sched interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout) { unsigned long flags; wait_queue_t wait; init_waitqueue_entry(&wait, current); current->state = TASK_INTERRUPTIBLE; sleep_on_head(q, &wait, &flags); timeout = schedule_timeout(timeout); sleep_on_tail(q, &wait, &flags); return timeout; }
類似於sleep_on_timeout()函數。進程在睡眠中可能在等待的時間沒有到達就被信號打斷而被喚醒,也可能是等待的時間到達而被喚醒。
以上四個函數都是讓進程在等待隊列上睡眠,不過是小有詫異而已。在實際用的過程中,根據需要選擇合適的函數使用就是了。例如在對軟驅數據的讀寫中,如果設備沒有就緒則調用sleep_on()函數睡眠直到數據可讀(可寫),在打開串口的時候,如果串口端口處於關閉狀態則調用interruptible_sleep_on()函數嚐試等待其打開。在聲卡驅動中,讀取聲音數據時,如果沒有數據可讀,就會等待足夠常的時間直到可讀取。
最後更新:2017-04-02 04:01:46