等待队列 赏析

本文深入解析了Linux系统中wait_event_interruptible函数的工作原理,包括如何响应条件、处理中断信号、进程状态转换及唤醒机制等核心内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述


这里写图片描述

/**
    如果被一个信号打断了,那么该函数将会返回-ERESTARTSYS
*/
#define wait_event_interruptible(wq, condition)             \
({                                  \
    int __ret = 0;  
    /**
        如果condition为false,
        那么__wait_event_interruptible将会被执行
    */                      \
    if (!(condition))                       \
        __wait_event_interruptible(wq, condition, __ret);   \
    __ret;                              \
})

#define __wait_event_interruptible(wq, condition, ret)          \
do {                                    \
    /**
        定义了一个wait_queue_t 类型的等待队列项_wait
    */
    DEFINE_WAIT(__wait);                        \
    {
        #define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
        #define DEFINE_WAIT_FUNC(name, function)                \
            wait_queue_t name = {                       \
                /**
                    private指向调用wait_event_interruptible的进程
                */
                .private    = current,              \
                /**
                    该进程被唤醒后会执行这个函数,初始化为默认autoremove_wake_function
                */
                .func       = function,             \
                .task_list  = LIST_HEAD_INIT((name).task_list), \
            }
    }
                                    \
    for (;;) {                          \
        /**
            这个函数主要做了一下事情:
                    1 :将上面定义的wait等待队列项放入到等待队列中
                    2 : 将进程的状态设置为 TASK_INTERRUPTIBLE
                    (这里与 wait_event_timeout不同,wait_event_timeout会将进程的状态设置为 TASK_UNINTERRUPTIBLE)

                    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);
                    }   
                    /**
                        设置进程状态为 TASK_INTERRUPTIBLE
                    */
                    set_current_state(state);
                    spin_unlock_irqrestore(&q->lock, flags);
        */
        prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);  \
        /**
            从上面的那个函数出来之后,就立即检查condition有没有满足,
            如果满足,那么就break,跳出for循环。
        */
        if (condition)                      \
            break;                      \
        /**
            看有没有信号等待处理,
            如果没有,那么就调用schedule() 放弃cpu,睡眠
        */  
        if (!signal_pending(current)) {             \
            schedule();                 \
            /**
                如果睡眠了,那么就继续for循环
            */
            continue;                   \
        }                           \
        /**
            看,如果有信号到来,会返回这个-ERESTARTSYS
        */
        ret = -ERESTARTSYS;                 \
        break;                          \
    }                               \
    /**
        进程被唤醒后调用的函数
        这个函数主要做了一下事情 : 
                1 : 将该进程的状态设置为TASK_RUNNING
                2 : 从等待队列中删除

            unsigned long flags;
            __set_current_state(TASK_RUNNING);
            if (!list_empty_careful(&wait->task_list)) 
            {
                spin_lock_irqsave(&q->lock, flags);
                list_del_init(&wait->task_list);
                spin_unlock_irqrestore(&q->lock, flags);
            }
    */
    finish_wait(&wq, &__wait);                  \
} while (0)

/**
   其实,就是调用队列中每个节点上的autoremove_wake_function()函数
   看它的第二个参数 : TASK_INTERRUPTIBLE,不能唤醒TASK_UNINTERRUPTIBLE状态的进程
   但是  wake_up() 则可以唤醒TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE状态的进程
   因为: #define wake_up(x)           __wake_up(x, TASK_NORMAL, 1, NULL)
   #define TASK_NORMAL      (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
*/
#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

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

/**
    真正干活的是这个函数
*/
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;

        /**
            对队列中的每个节点,都会调用func,这个func就是autoremove_wake_function()函数,
            是再wait_event_interruptible中绑定的。
            flags & WQ_FLAG_EXCLUSIVE :?
            --nr_exclusive  : 若设置为1 ,那么只会唤醒一个,其余的还在睡。
        */
        if (curr->func(curr, mode, wake_flags, key) &&(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
        {
            break;
        }   
    }
}

int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
    int ret = default_wake_function(wait, mode, sync, key);

    /**
        这里ret 就是 再try_to_wake_up()函数中定义的局部变量success的值
        如果success为1,那么 将会return 1 ,唤醒成功。
    */
    if (ret)
    {
        list_del_init(&wait->task_list);
    }   
    return ret;
}

int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags,void *key)
{
    return try_to_wake_up(curr->private, mode, wake_flags);
}

static int try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
    /**
        success这个局部变量的值 会决定了唤醒的成功与否,
        而它的值,是由传入的进程状态和待唤醒的进程的状态是否一致决定的。
    */
    int cpu, success = 0;
    ...
    /**
        要唤醒的进程的状态 与 传入的状态比较,
        如果一致,那么不会执行goto out
        那么success 会被赋值为 1,否则success还是为 0,
        那么再
    */
   if (!(p->state & state))
   {
        goto out;
    }   
    success = 1;
    ...
    ...
    out:
        raw_spin_unlock_irqrestore(&p->pi_lock, flags);
        return success;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值