首页 > 代码库 > wait_event族函数浅析

wait_event族函数浅析

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族函数浅析