linux等待隊(duì)列wait_queue_head_t和wait_queue_t

學(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)突梦。

等待隊(duì)列.jpg

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è)人博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炼绘,一起剝皮案震驚了整個(gè)濱河市企软,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌饭望,老刑警劉巖仗哨,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件形庭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡厌漂,警方通過(guò)查閱死者的電腦和手機(jī)萨醒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)苇倡,“玉大人富纸,你說(shuō)我怎么就攤上這事≈冀罚” “怎么了晓褪?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)综慎。 經(jīng)常有香客問(wèn)我涣仿,道長(zhǎng),這世上最難降的妖魔是什么示惊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任好港,我火速辦了婚禮,結(jié)果婚禮上米罚,老公的妹妹穿的比我還像新娘钧汹。我一直安慰自己,他們只是感情好录择,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布拔莱。 她就那樣靜靜地躺著,像睡著了一般隘竭。 火紅的嫁衣襯著肌膚如雪辨宠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天货裹,我揣著相機(jī)與錄音,去河邊找鬼精偿。 笑死弧圆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笔咽。 我是一名探鬼主播搔预,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼叶组!你這毒婦竟也來(lái)了拯田?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤甩十,失蹤者是張志新(化名)和其女友劉穎船庇,沒(méi)想到半個(gè)月后吭产,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸭轮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年臣淤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窃爷。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡邑蒋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出按厘,到底是詐尸還是另有隱情医吊,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布逮京,位于F島的核電站卿堂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏造虏。R本人自食惡果不足惜御吞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漓藕。 院中可真熱鬧陶珠,春花似錦、人聲如沸享钞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)栗竖。三九已至暑脆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狐肢,已是汗流浹背添吗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留份名,地道東北人碟联。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像僵腺,于是被迫代替她去往敵國(guó)和親鲤孵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容