linux completion

一. linux kernel阻塞等待事件方法:
    把线程置为可中断或者不可中断状态
    然后调用进程调度函数执行其他代码
    该进程代码再次执行的情况:
    1) 调度超时
    2) 进程被等待事件唤醒
    3) 进程设置可中断状态,被某个信号中断了
    4) CPU空闲调度
    此时需要判断进程是否被等待事件唤醒, 是的话则返回. 否则重复上述步骤.

二. linux kernel等待事件唤醒
    调用 try_to_wake_up(task, TASK_NORMAL, 0) 函数即可唤醒进程.

三. 为了能够实现多个进程等待一个资源, 就需要一个等待队列以及一个计数器.
    计数器用来指示要唤醒多少个等待资源的进程.
    每个等待资源的进程都会添加到等待队列中, 包含标志,回调函数以及私有数据. 一般来说, 私有数据是进程的结构体, 回调函数是default_wake_function(用来唤醒进程).

struct completion {
    unsigned int done;          // 唤醒几个进程
    wait_queue_head_t wait;     // 等待队列头
};

struct __wait_queue_head {
    spinlock_t lock;            // 用来保护链表
    struct list_head task_list; // 链表头
};
typedef struct __wait_queue_head wait_queue_head_t;

// 实际等待队列的结构体, 标志, 回调, 私有数据, 链表
struct __wait_queue {
    unsigned int flags;
    void *private;              // task_struct
    wait_queue_func_t func;     // default_wake_function 唤醒进程
    struct list_head task_list; // 链表
};

complete 唤醒函数的实现:
void complete(struct completion *x)
{
    unsigned long flags;

    spin_lock_irqsave(&x->wait.lock, flags);
    x->done++;      // 唤醒单个进程
    __wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL);
    spin_unlock_irqrestore(&x->wait.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;

        if (curr->func(curr, mode, wake_flags, key) &&
                (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
            break;
    }
}


========================
wait_for_completion_timeout 等待资源函数实现:
unsigned long
wait_for_completion_timeout(struct completion *x, unsigned long timeout)
{
    // 设置进程为不可中断状态
    return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);
}
static long wait_for_common(struct completion *x, long timeout, int state)
{
    spin_lock_irq(&x->wait.lock);
    timeout = do_wait_for_common(x, timeout, state);
    spin_unlock_irq(&x->wait.lock);
    return timeout;
}

static inline long
do_wait_for_common(struct completion *x, long timeout, int state)
{
    if (!x->done) {
        // 申明wait_queue_t结构体宏, 设置私有结构体为本进程task, 回调为唤醒函数
        DECLARE_WAITQUEUE(wait, current);

        wait.flags |= WQ_FLAG_EXCLUSIVE;
        // 添加到等待队列链表
        __add_wait_queue_tail(&x->wait, &wait);
        do {
            if (signal_pending_state(state, current)) {
                timeout = -ERESTARTSYS;
                break;
            }
            __set_current_state(state);
            spin_unlock_irq(&x->wait.lock);
            timeout = schedule_timeout(timeout);
            spin_lock_irq(&x->wait.lock);

        // 没唤醒, 也没超时则循环等待
        } while (!x->done && timeout);

        // 退出等待队列
        __remove_wait_queue(&x->wait, &wait);
        if (!x->done)
            return timeout;
    }

    // 消耗一个资源
    x->done--;
    return timeout ?: 1;
}



================================ 简单例子 ========================================
// 申明completion结构体, 并初始化锁等资源
DECLARE_COMPLETION(work);

进程上下文, 等待资源
static void wait_resource()
{
    // 由于这个函数使用局部变量的等待队列, 所以它是可重入的.
    wait_for_completion_timeout(&work, msecs_to_jiffies(1000));
}

// 中断函数, 唤醒等待的进程
static irqreturn_t irq_handler(int irq, void *cookie)
{
    complete(work);
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值