學(xué)號(hào):19021211263
等待隊(duì)列在linux內(nèi)核中有著舉足輕重的作用示血,很多l(xiāng)inux驅(qū)動(dòng)都或多或少涉及到了等待隊(duì)列腋腮。因此宴卖,對(duì)于linux內(nèi)核及驅(qū)動(dòng)開(kāi)發(fā)者來(lái)說(shuō)席纽,掌握等待隊(duì)列是必須課之一赖欣。 Linux內(nèi)核的等待隊(duì)列是以雙循環(huán)鏈表為基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)屑彻,與進(jìn)程調(diào)度機(jī)制緊密結(jié)合,能夠用于實(shí)現(xiàn)核心的異步事件通知機(jī)制顶吮。它有兩種數(shù)據(jù)結(jié)構(gòu):等待隊(duì)列頭(wait_queue_head_t)和等待隊(duì)列項(xiàng)(wait_queue_t)社牲。等待隊(duì)列頭和等待隊(duì)列項(xiàng)中都包含一個(gè)list_head類型的域作為”連接件”。它通過(guò)一個(gè)雙鏈表和把等待task的頭云矫,和等待的進(jìn)程列表鏈接起來(lái)膳沽。下面具體介紹。
一让禀、定義:
頭文件:/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;
二挑社、作用:
在內(nèi)核里面,等待隊(duì)列是有很多用處的巡揍,尤其是在中斷處理痛阻、進(jìn)程同步、定時(shí)等場(chǎng)合腮敌≮宓保可以使用等待隊(duì)列在實(shí)現(xiàn)阻塞進(jìn)程的喚醒俏扩。它以隊(duì)列為基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),與進(jìn)程調(diào)度機(jī)制緊密結(jié)合弊添,能夠用于實(shí)現(xiàn)內(nèi)核中的異步事件通知機(jī)制录淡,同步對(duì)系統(tǒng)資源的訪問(wèn)等。
三油坝、字段詳解:
1嫉戚、spinlock_t lock;
在對(duì)task_list與操作的過(guò)程中,使用該鎖實(shí)現(xiàn)對(duì)等待隊(duì)列的互斥訪問(wèn)澈圈。
2彬檀、srtuct list_head_t task_list;
雙向循環(huán)鏈表,存放等待的進(jìn)程瞬女。
四窍帝、操作:
1、定義并初始化:
(1)
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
直接定義并初始化诽偷。init_waitqueue_head()函數(shù)會(huì)將自旋鎖初始化為未鎖坤学,等待隊(duì)列初始化為空的雙向循環(huán)鏈表。
(2)
DECLARE_WAIT_QUEUE_HEAD(my_queue);
定義并初始化报慕,相當(dāng)于(1)拥峦。
(3) 定義等待隊(duì)列:
DECLARE_WAITQUEUE(name,tsk);
注意此處是定義一個(gè)wait_queue_t類型的變量name,并將其private與設(shè)置為tsk卖子。wait_queue_t類型定義如下:
typedef struct __wait_queue 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域指明該等待的進(jìn)程是互斥進(jìn)程還是非互斥進(jìn)程。其中0是非互斥進(jìn)程刑峡,WQ_FLAG_EXCLUSIVE(0x01)是互斥進(jìn)程洋闽。等待隊(duì)列(wait_queue_t)和等待對(duì)列頭(wait_queue_head_t)的區(qū)別是等待隊(duì)列是等待隊(duì)列頭的成員。也就是說(shuō)等待隊(duì)列頭的task_list域鏈接的成員就是等待隊(duì)列類型的(wait_queue_t)突梦。
2诫舅、(從等待隊(duì)列頭中)添加/移出等待隊(duì)列:
(1) add_wait_queue()函數(shù):
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);
}
EXPORT_SYMBOL(add_wait_queue);
設(shè)置等待的進(jìn)程為非互斥進(jìn)程,并將其添加進(jìn)等待隊(duì)列頭(q)的隊(duì)頭中宫患。
void 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);
}
EXPORT_SYMBOL(add_wait_queue_exclusive);
該函數(shù)也和add_wait_queue()函數(shù)功能基本一樣刊懈,只不過(guò)它是將等待的進(jìn)程(wait)設(shè)置為互斥進(jìn)程。
(2)remove_wait_queue()函數(shù):
void 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);
}
EXPORT_SYMBOL(remove_wait_queue);
在等待的資源或事件滿足時(shí)娃闲,進(jìn)程被喚醒虚汛,使用該函數(shù)被從等待頭中刪除。
3皇帮、等待事件:
(1)wait_event()宏:
/**
* wait_event - sleep until a condition gets true
* @wq: the waitqueue to wait on
* @condition: a C expression for the event to wait for
*
* The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
* @condition evaluates to true. The @condition is checked each time
* the waitqueue @wq is woken up.
*
* wake_up() has to be called after changing any variable that could
* change the result of the wait condition.
*/
#define wait_event(wq, condition) \
do { \
if (condition) \
break; \
__wait_event(wq, condition); \
} while (0)
在等待會(huì)列中睡眠直到condition為真卷哩。在等待的期間,進(jìn)程會(huì)被置為T(mén)ASK_UNINTERRUPTIBLE進(jìn)入睡眠属拾,直到condition變量變?yōu)檎娼辍C看芜M(jìn)程被喚醒的時(shí)候都會(huì)檢查condition的值.
(2)wait_event_interruptible()函數(shù):
和wait_event()的區(qū)別是調(diào)用該宏在等待的過(guò)程中當(dāng)前進(jìn)程會(huì)被設(shè)置為T(mén)ASK_INTERRUPTIBLE狀態(tài).在每次被喚醒的時(shí)候,首先檢查condition是否為真,如果為真則返回,否則檢查如果進(jìn)程是被信號(hào)喚醒,會(huì)返回-ERESTARTSYS錯(cuò)誤碼.如果是condition為真,則返回0.
(3)wait_event_timeout()宏:
也與wait_event()類似.不過(guò)如果所給的睡眠時(shí)間為負(fù)數(shù)則立即返回.如果在睡眠期間被喚醒,且condition為真則返回剩余的睡眠時(shí)間,否則繼續(xù)睡眠直到到達(dá)或超過(guò)給定的睡眠時(shí)間,然后返回0.
(4)wait_event_interruptible_timeout()宏:
與wait_event_timeout()類似,不過(guò)如果在睡眠期間被信號(hào)打斷則返回ERESTARTSYS錯(cuò)誤碼.
(5) wait_event_interruptible_exclusive()宏
同樣和wait_event_interruptible()一樣,不過(guò)該睡眠的進(jìn)程是一個(gè)互斥進(jìn)程.
4冷溶、喚醒隊(duì)列:
(1)wake_up()函數(shù):
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
/**
* __wake_up - wake up threads blocked on a waitqueue.
* @q: the waitqueue
* @mode: which threads
* @nr_exclusive: how many wake-one or wake-many threads to wake up
* @key: is directly passed to the wakeup function
*/
void __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);
}
EXPORT_SYMBOL(__wake_up);
喚醒等待隊(duì)列.可喚醒處于TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE狀態(tài)的進(jìn)程,和wait_event/wait_event_timeout成對(duì)使用.
(2)wake_up_interruptible()函數(shù):
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
和wake_up()唯一的區(qū)別是它只能喚醒TASK_INTERRUPTIBLE狀態(tài)的進(jìn)程.,與wait_event_interruptible/wait_event_interruptible_timeout/ wait_event_interruptible_exclusive成對(duì)使用.
(3)
#define wake_up_all(x) __wake_up(x, TASK_NORMAL, 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一樣.
5、在等待隊(duì)列上睡眠:
(1) sleep_on()函數(shù):
void __sched sleep_on(wait_queue_head_t *q)
{
sleep_on_common(q, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
static long __sched
sleep_on_common(wait_queue_head_t *q, int state, long timeout)
{
unsigned long flags;
wait_queue_t wait;
init_waitqueue_entry(&wait, current);
__set_current_state(state);
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue(q, &wait);
spin_unlock(&q->lock);
timeout = schedule_timeout(timeout);
spin_lock_irq(&q->lock);
__remove_wait_queue(q, &wait);
spin_unlock_irqrestore(&q->lock, flags);
return timeout;
}
該函數(shù)的作用是定義一個(gè)等待隊(duì)列(wait)尊浓,并將當(dāng)前進(jìn)程添加到等待隊(duì)列中(wait)逞频,然后將當(dāng)前進(jìn)程的狀態(tài)置為T(mén)ASK_UNINTERRUPTIBLE,并將等待隊(duì)列(wait)添加到等待隊(duì)列頭(q)中栋齿。之后就被掛起直到資源可以獲取苗胀,才被從等待隊(duì)列頭(q)中喚醒,從等待隊(duì)列頭中移出褒颈。在被掛起等待資源期間柒巫,該進(jìn)程不能被信號(hào)喚醒。
(2)sleep_on_timeout()函數(shù):
long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout)
{
return sleep_on_common(q, TASK_UNINTERRUPTIBLE, timeout);
}
EXPORT_SYMBOL(sleep_on_timeout);
與sleep_on()函數(shù)的區(qū)別在于調(diào)用該函數(shù)時(shí)谷丸,如果在指定的時(shí)間內(nèi)(timeout)沒(méi)有獲得等待的資源就會(huì)返回堡掏。實(shí)際上是調(diào)用schedule_timeout()函數(shù)實(shí)現(xiàn)的。值得注意的是如果所給的睡眠時(shí)間(timeout)小于0刨疼,則不會(huì)睡眠泉唁。該函數(shù)返回的是真正的睡眠時(shí)間。
(3)interruptible_sleep_on()函數(shù):
void __sched interruptible_sleep_on(wait_queue_head_t *q)
{
sleep_on_common(q, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
EXPORT_SYMBOL(interruptible_sleep_on);
該函數(shù)和sleep_on()函數(shù)唯一的區(qū)別是將當(dāng)前進(jìn)程的狀態(tài)置為T(mén)ASK_INTERRUPTINLE揩慕,這意味在睡眠如果該進(jìn)程收到信號(hào)則會(huì)被喚醒亭畜。
(4)interruptible_sleep_on_timeout()函數(shù):
long __sched
interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout)
{
return sleep_on_common(q, TASK_INTERRUPTIBLE, timeout);
}
EXPORT_SYMBOL(interruptible_sleep_on_timeout);
類似于sleep_on_timeout()函數(shù)。進(jìn)程在睡眠中可能在等待的時(shí)間沒(méi)有到達(dá)就被信號(hào)打斷而被喚醒迎卤,也可能是等待的時(shí)間到達(dá)而被喚醒拴鸵。
以上四個(gè)函數(shù)都是讓進(jìn)程在等待隊(duì)列上睡眠,不過(guò)是小有詫異而已蜗搔。在實(shí)際用的過(guò)程中劲藐,根據(jù)需要選擇合適的函數(shù)使用就是了。例如在對(duì)軟驅(qū)數(shù)據(jù)的讀寫(xiě)中樟凄,如果設(shè)備沒(méi)有就緒則調(diào)用sleep_on()函數(shù)睡眠直到數(shù)據(jù)可讀(可寫(xiě))聘芜,在打開(kāi)串口的時(shí)候,如果串口端口處于關(guān)閉狀態(tài)則調(diào)用interruptible_sleep_on()函數(shù)嘗試等待其打開(kāi)缝龄。在聲卡驅(qū)動(dòng)中汰现,讀取聲音數(shù)據(jù)時(shí),如果沒(méi)有數(shù)據(jù)可讀叔壤,就會(huì)等待足夠常的時(shí)間直到可讀取瞎饲。
更多有趣內(nèi)容歡迎訪問(wèn)我的個(gè)人博客。