等待队列

等待队列如何使用?分两步:

       1. 为了使得等待进程在一个等待队列中睡眠,需要调用函数wait_event()函数。进程进入睡眠,将控制权释放给调度器。

       2. 在内核中另一处,调用wake_up()函数唤醒等待队列中的睡眠进程。

注:使用wait_event()函数使得进程睡眠;而在内核另一处有一个对应的wake_up()函数被调用。


(一)初始化等待队列元素

        有两种方法初始化队列:

        1. 动态初始化init_waitqueue_entry()

    static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)  
    {  
        q->flags = 0;  
        q->private = p;  
        q->func = default_wake_function;  
    } 


        2. 静态初始化DEFINE_WAIT()

    #define DEFINE_WAIT_FUNC(name, function)                \  
        wait_queue_t name = {                       \  
            .private    = current,              \  
            .func       = function,             \  
            .task_list  = LIST_HEAD_INIT((name).task_list), \  
        }  
      
    #define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)  

       其中函数autoremove_wake_function()是用来唤醒进程的,该函数不经调用default_wake_function(),还将所属等待队列成员从等待队列删除。


static DECLARE_WAIT_QUEUE_HEAD(bat_thread_wq);

(二)进程睡眠

1. 通过add_wait_queue()函数将一个进程添加到等待队列,首先获得队列的自旋锁,然后调用__add_wait_queue()实现将新的等待进程添加等待队列(添加到等待队列的头部),然后解锁;
 通常情况下,add_wait_queue()函数不会直接使用,因为add_wait_queue()函数不与具体的逻辑相管理,单纯的一个等待队列的模型是没有意义的,因此通常使用的是wait_event()函数:


    #define wait_event(wq, condition)                   \  
    do {                                    \  
        if (condition)                          \  
            break;                          \  
        __wait_event(wq, condition);                    \  
    } while (0)  

函数__wait_event()

    #define __wait_event(wq, condition)                     \  
    do {                                    \  
        DEFINE_WAIT(__wait);                        \  
                                        \  
        for (;;) {                          \  
            prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);    \  
            if (condition)                      \  
                break;                      \  
            schedule();                     \  
        }                               \  
        finish_wait(&wq, &__wait);                  \  
    } while (0)  

其中wq是等待进程需要加入的等待队列,而condition是通过与所等待时间有关的一个C表达式形式给出。表示,条件满足时,可以立即停止处理。


主要工作由__wait_event()来完成:

       (1) 调用DEFINE_WAIT宏创建等待队列成员;
       (2) 使用一个无线循环,在循环体内,
(a) 调用prepare_to_wait()使得进程在等待队列上等待,并将进程状态置为不可中断TASK_UNINTERRUPTIBLE;
(b) 当进程被唤醒时,检查指定的条件condition是否满足,如果满足则跳出循环,否则将控制权交给调度器,然后进程继续睡眠。
        (3) 调用函数finish_wait()将进程状态设置为TASK_RUNNING,并从等待队列的链表中移除对应的成员。

       其他与wait_event类似的函数:
       1. wait_event_interrupable()函数 ,使得进程处于可中断(TASK_INTERRUPTIBLE)状态,从而睡眠进程可以通过接收信号被唤醒;
       2. wait_event_timeout()函数,等待满足指定的条件,但是如果等待时间超过指定的超时限制则停止睡眠,可以防止进程永远睡眠;
       3. wait_event_interruptible_timeout() 使得进程睡眠,不但可以通过接收信号被唤醒,也具有超时限制。

(三)进程唤醒

内核中虽然定义了很多唤醒等待队列中进程的函数,但是最终调用的都是__wake_up()


    #define wake_up(x)          __wake_up(x, TASK_NORMAL, 1, NULL)  
    #define wake_up_nr(x, nr)       __wake_up(x, TASK_NORMAL, nr, NULL)  
    #define wake_up_all(x)          __wake_up(x, TASK_NORMAL, 0, NULL)  
    #define wake_up_locked(x)       __wake_up_locked((x), TASK_NORMAL)  
      
    #define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, 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)  
    #define wake_up_interruptible_sync(x)   __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)  
 
    而__wake_up()函数在加锁之后调用的是__wake_up_common()







static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,  
                int nr_exclusive, int wake_flags, void *key)  
    {  
        wait_queue_t *curr, *next;  
      
        list_for_each_entry_safe(curr, next, &q->task_list, task_list) {  
            unsigned flags = curr->flags;  
      
            if (curr->func(curr, mode, wake_flags, key) &&  
                    (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)  
                break;  
        }  
       其中:q是等待队列,mode指定进程的状态,用于控制唤醒进程的条件,nr_exclusive表示将要唤醒的设置了WQ_FLAG_EXCLUSIVE标志的进程的数目。

        然后扫描链表,调用func(注册的进程唤醒函数,默认为default_wake_function)唤醒每一个进程,直至队列为空,或者没有更多的进程被唤醒,或者被唤醒的的独占进程数目已经达到规定数目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值