http://blog.sina.com.cn/s/blog_6a1837e90100pizf.html
2011
一、定義:
/include/linux/wait.h
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_headwait_queue_head_t;
二、作用:
在內核里面,等待隊列是有很多用處的,尤其是在中斷處理、進(jìn)程同步、定時(shí)等場(chǎng)合??梢允褂玫却犃性趯?shí)現阻塞進(jìn)程的喚醒。它以隊列為基礎數據結構,與進(jìn)程調度機制緊密結合,能夠用于實(shí)現內核中的異步事件通知機制,同步對系統資源的訪(fǎng)問(wèn)等。
三、字段詳解:
1、spinlock_t lock;
在對task_list與操作的過(guò)程中,使用該鎖實(shí)現對等待隊列的互斥訪(fǎng)問(wèn)。
2、srtuct list_head_t task_list;
雙向循環(huán)鏈表,存放等待的進(jìn)程。
三、操作:
1、定義并初始化:
(1)
wait_queue_head_tmy_queue;
init_waitqueue_head(&my_queue);
直接定義并初始化。init_waitqueue_head()函數會(huì )將自旋鎖初始化為未鎖,等待隊列初始化為空的雙向循環(huán)鏈表。
(2)
DECLARE_WAIT_QUEUE_HEAD(my_queue);
定義并初始化,相當于(1)。
(3)定義等待隊列:
DECLARE_WAITQUEUE(name,tsk);
注意此處是定義一個(gè)wait_queue_t類(lèi)型的變量name,并將其private域設置為tsk。wait_queue_t類(lèi)型定義如下:
struct __wait_queue{
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
其中flags域指明該等待的進(jìn)程是互斥進(jìn)程還是非互斥進(jìn)程。其中0是非互斥進(jìn)程,WQ_FLAG_EXCLUSIVE(0x01)是互斥進(jìn)程。等待隊列(wait_queue_t)和等待對列頭(wait_queue_head_t)的區別是等待隊列是等待隊列頭的成員。也就是說(shuō)等待隊列頭的task_list域鏈接的成員就是等待隊列類(lèi)型的(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);
}
設置等待的進(jìn)程為非互斥進(jìn)程,并將其添加進(jìn)等待隊列頭(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()函數功能基本一樣,只不過(guò)它是將等待的進(jìn)程(wait)設置為互斥進(jìn)程。
(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);
}
在等待的資源或事件滿(mǎn)足時(shí),進(jìn)程被喚醒,使用該函數從等待頭中刪除。
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為真。在等待的期間,進(jìn)程會(huì )被置為T(mén)ASK_UNINTERRUPTIBLE進(jìn)入睡眠,直到condition變量變?yōu)檎?。每次進(jìn)程被喚醒的時(shí)候都會(huì )檢查condition的值.
(2)wait_event_interruptible()函數:
和wait_event()的區別是調用該宏在等待的過(guò)程中當前進(jìn)程會(huì )被設置為T(mén)ASK_INTERRUPTIBLE狀態(tài).在每次被喚醒的時(shí)候,首先檢查condition是否為真,如果為真則返回0,否則檢查如果進(jìn)程是被信號喚醒,會(huì )返回-ERESTARTSYS錯誤碼.
(3)wait_event_timeout()宏:
也與wait_event()類(lèi)似.不過(guò)如果所給的睡眠時(shí)間為負數則立即返回.如果在睡眠期間被喚醒,且condition為真則返回剩余的睡眠時(shí)間,否則繼續睡眠直到到達或超過(guò)給定的睡眠時(shí)間,然后返回0.
(4)wait_event_interruptible_timeout()宏:
與wait_event_timeout()類(lèi)似,不過(guò)如果在睡眠期間被信號打斷且condition不為真,則返回ERESTARTSYS錯誤碼.
(5) wait_event_interruptible_exclusive()宏
同樣和wait_event_interruptible()一樣,不過(guò)該睡眠的進(jìn)程是一個(gè)互斥進(jìn)程.
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 intmode,
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狀態(tài)的進(jìn)程,和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狀態(tài)的進(jìn)程.,與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);
}
該函數的作用是定義一個(gè)等待隊列(wait),并將當前進(jìn)程添加到等待隊列中(wait),然后將當前進(jìn)程的狀態(tài)置為T(mén)ASK_UNINTERRUPTIBLE,并將等待隊列(wait)添加到等待隊列頭(q)中。之后就被掛起直到資源可以獲取,才被從等待隊列頭(q)中喚醒,從等待隊列頭中移出。在被掛起等待資源期間,該進(jìn)程不能被信號喚醒。
(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()函數的區別在于調用該函數時(shí),如果在指定的時(shí)間內(timeout)沒(méi)有獲得等待的資源就會(huì )返回。實(shí)際上是調用schedule_timeout()函數實(shí)現的。值得注意的是如果所給的睡眠時(shí)間(timeout)小于0,則不會(huì )睡眠。該函數返回的是真正的睡眠時(shí)間。
(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()函數唯一的區別是將當前進(jìn)程的狀態(tài)置為T(mén)ASK_INTERRUPTINLE,這意味在睡眠如果該進(jìn)程收到信號則會(huì )被喚醒。
(4)interruptible_sleep_on_timeout()函數:
long__sched
interruptible_sleep_on_timeout(wait_queue_head_t *q, longtimeout)
{
unsigned longflags;
wait_queue_twait;
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);
returntimeout;
}
類(lèi)似于sleep_on_timeout()函數。進(jìn)程在睡眠中可能在等待的時(shí)間沒(méi)有到達就被信號打斷而被喚醒,也可能是等待的時(shí)間到達而被喚醒。
以上四個(gè)函數都是讓進(jìn)程在等待隊列上睡眠,不過(guò)是小有差異而已。在實(shí)際用的過(guò)程中,根據需要選擇合適的函數使用就是了。例如在對軟驅數據的讀寫(xiě)中,如果設備沒(méi)有就緒則調用sleep_on()函數睡眠直到數據可讀(可寫(xiě)),在打開(kāi)串口的時(shí)候,如果串口端口處于關(guān)閉狀態(tài)則調用interruptible_sleep_on()函數嘗試等待其打開(kāi)。在聲卡驅動(dòng)中,讀取聲音數據時(shí),如果沒(méi)有數據可讀,就會(huì )等待足夠長(cháng)的時(shí)間直到可讀取。