1. 程式人生 > >wait_event族函數淺析

wait_event族函數淺析

spin efi 返回 條件 exc 進行 add prepare color

2017-06-03


周末閑暇無事,聊聊內核中的wait_event*類函數的具體實現,等待事件必定涉及到某個條件,而這些函數的區別主要是等待後喚醒的方式……直奔主題,上源碼

wait_event_interruptible

#define wait_event_interruptible(wq, condition)                \
({                                        int __ret = 0;                                if (!(condition))                                __wait_event_interruptible(wq, condition, __ret);        __ret;                                })

調用該宏首先會先檢查條件,如果條件已經滿足,則不用等了呀,返回吧……,否則調用__wait_event_interruptible

#define __wait_event_interruptible(wq, condition, ret)            do {                                        DEFINE_WAIT(__wait);                                                                for (;;) {                                    prepare_to_wait(
&wq, &__wait, TASK_INTERRUPTIBLE); if (condition) break; if (!signal_pending(current)) { schedule(); continue; } ret
= -ERESTARTSYS; break; } finish_wait(&wq, &__wait); } while (0)

首先聲明了一個關聯當前進程的wait對象,然後進入一個for空循環,開始就調用prepare_to_wait,該函數代碼如下,功能就是把wait對象加入到等待隊列並設置當前進程的狀態,註意此刻僅僅是設置了結構體的狀態,並沒有觸發調度。在真正觸發調度之前,需要再次檢查條件是否滿足,如果滿足了,直接break,否則檢查當前進程是否有信號存在,因為該宏設置的等待是可以被信號喚醒的,如果有信號則同樣break。否則就調用schedule進行調度吧!其他種類的額wait函數都是同樣的結構,區別就在於for循環內內部的不同,所以後續主要列舉的是for循環的代碼。

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);
}

wait_event_interruptible_timeout

該函數和前面類似,但是增加了等待時間,還是簡單看下__wait_event_interruptible_timeout的for循環

for (;;) {                                    prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);            if (condition)                                    break;                                if (!signal_pending(current)) {                            ret = schedule_timeout(ret);                        if (!ret)                                    break;                                continue;                            }                                    ret = -ERESTARTSYS;                            break;                                }            

在沒有信號的狀態下,是調用了schedule_timeout函數,該函數在調度之前會對當前進程設定一個定時器,並設定process_timeout回調函數用於時間到了就執行該函數,自然該函數就是完成喚醒進程的功能。關於定時器,本文不做介紹。

wait_event_timeout

該函數和上面區別就是沒有了對信號的判斷,並且設置進程狀態為TASK_UNINTERRUPTIBLE,其__wait_event_timeout中for循環如下

for (;;) {                                    prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);            if (condition)                                    break;                                ret = schedule_timeout(ret);                        if (!ret)                                    break;                            }        

wait_event

該函數是最簡單的,設置狀態TASK_UNINTERRUPTIBLE,進行條件判斷,如果不符合就調度。沒有其他額外的操作。

以馬內利!

參考:linux3.10.1源碼

wait_event族函數淺析