完成量同步机制学习

一、定义:

linux/include/linux/completion.h

  13struct completion {
  14        unsigned int done;
  15        wait_queue_head_t wait;
  16};

二、作用:

虽然信号量可以用于实现同步,但往往可能会出现一些不好的结果。例如:当进程A分配了一个临时信号量变量,把它初始化为关闭的MUTEX,并把其地址传递给进程B,然后在A之上调用down(),进程A打算一旦被唤醒就撤销给信号量。随后,运行在不同CPU上的进程B在同一个信号量上调用up()。然而,up()down()的目前实现还允许这两个函数在同一个信号量上并发。因此,进程A可以被唤醒并撤销临时信号量,而进程B还在运行up()函数。结果up()可能试图访问一个不存在的数据结构。这样就会出现错误。为了防止发生这种错误就专门设计了completion机制专门用于同步。

三、个字段详解:

1unsigned int done;

指示等待的事件是否完成。初始化时为0。如果为0,则表示等待的事件未完成。大于0表示等待的事件已经完成。

2wait_queue_head_t wait;

存放等待该事件完成的进程队列。

四、操作:

1、定义和初始化:

(1)

struct completion completion;

init_completion(&completion);

直接定义并调用init_completion()初始化。init_completion()会将done字段初始化为0wait字段的自旋锁为未锁,等待队列为空。这说明调用该完成量的进程必须等待某事件完成(即另外一进程必须先调用completiom()唤醒该完成量)

(2)

DECLARE_COMPLETION(completion);

直接定义并初始化completion完成量,效果等同于以上定义方式。

2、等待完成量:

(1)wait_for_completion()函数,/linux/kernel/sched.c

3731void fastcall __sched wait_for_completion(struct completion *x)
3732{
3733        might_sleep();
3734
3735        spin_lock_irq(&x->wait.lock);
3736        if (!x->done) {
3737                DECLARE_WAITQUEUE(wait, current);
3738
3739                wait.flags |= WQ_FLAG_EXCLUSIVE;
3740                __add_wait_queue_tail(&x->wait, &wait);
3741                do {
3742                        __set_current_state(TASK_UNINTERRUPTIBLE);
3743                        spin_unlock_irq(&x->wait.lock);
3744                        schedule();
3745                        spin_lock_irq(&x->wait.lock);
3746                } while (!x->done);
3747                __remove_wait_queue(&x->wait, &wait);
3748        }
3749        x->done--;
3750        spin_unlock_irq(&x->wait.lock);
3751}

该函数相当于信号量中的down()操作。不过在操作中对使用其自身的自旋锁。如果done0,则说明等待的事件没有完成,则调用DECLARE_WAITQUEUE()定义等待队列wait并将当前进程添加进等待队列wait。然后将wait添加进该完成量的等待队列的末尾,进入循环。设置当前进程为不可中断状态(TASK_UNINTERRUPTIBLE),释放自旋锁并让当前进程进入睡眠状态。一旦进程被调度唤醒据又获得自旋锁并查看等待的事件是否完成。如果完成(大于0),则从完成量的等待队列中删除等待的进程,并自减 done,释放自旋锁,从等待状态返回继续运行。否则继续睡眠。如果done于大0,则说明等待的事件已经完成,则自减done,直接返回。

(2)wait_for_completion_timeout()函数,/linux/kernel/sched.c

3754unsigned long fastcall __sched
3755wait_for_completion_timeout(struct completion *x, unsigned long timeout)
3756{
3757        might_sleep();
3758
3759        spin_lock_irq(&x->wait.lock);
3760        if (!x->done) {
3761                DECLARE_WAITQUEUE(wait, current);
3762
3763                wait.flags |= WQ_FLAG_EXCLUSIVE;
3764                __add_wait_queue_tail(&x->wait, &wait);
3765                do {
3766                        __set_current_state(TASK_UNINTERRUPTIBLE);
3767                        spin_unlock_irq(&x->wait.lock);
3768                        timeout = schedule_timeout(timeout);
3769                        spin_lock_irq(&x->wait.lock);
3770                        if (!timeout) {
3771                                __remove_wait_queue(&x->wait, &wait);
3772                                goto out;
3773                        }
3774                } while (!x->done);
3775                __remove_wait_queue(&x->wait, &wait);
3776        }
3777        x->done--;
3778out:
3779        spin_unlock_irq(&x->wait.lock);
3780        return timeout;
3781}

也是等待完成量。与wait_for_completion()最大的区别是它等待超时的情况下返回。也就是说如果经过给定的时间该完成量还没有被唤醒,就直接返回。这样最大的好处是经过一定的时间该进程已经不需要等待某事件,那么就可以直接被唤醒继续执行。

(3)wait_for_completion_interruptible()函数,/linux/kernel/sched.c

3784int fastcall __sched wait_for_completion_interruptible(struct completion *x)
3785{
3786        int ret = 0;
3787
3788        might_sleep();
3789
3790        spin_lock_irq(&x->wait.lock);
3791        if (!x->done) {
3792                DECLARE_WAITQUEUE(wait, current);
3793
3794                wait.flags |= WQ_FLAG_EXCLUSIVE;
3795                __add_wait_queue_tail(&x->wait, &wait);
3796                do {
3797                        if (signal_pending(current)) {
3798                                ret = -ERESTARTSYS;
3799                                __remove_wait_queue(&x->wait, &wait);
3800                                goto out;
3801                        }
3802                        __set_current_state(TASK_INTERRUPTIBLE);
3803                        spin_unlock_irq(&x->wait.lock);
3804                        schedule();
3805                        spin_lock_irq(&x->wait.lock);
3806                } while (!x->done);
3807                __remove_wait_queue(&x->wait, &wait);
3808        }
3809        x->done--;
3810out:
3811        spin_unlock_irq(&x->wait.lock);
3812
3813        return ret;
3814}

可见这中等待完成量的方式是可以被信号打断的。如果当前进程收到 如果收到TIF_SIGPENDING信号,则等待该完成量的进程会被从等待队列中删除,并返回ERESTARTSYS

(4)wait_for_completion_interruptible_timeout()函数,/linux/kernel/sched.c

3817unsigned long fastcall __sched
3818wait_for_completion_interruptible_timeout(struct completion *x,
3819                                          unsigned long timeout)
3820{
3821        might_sleep();
3822
3823        spin_lock_irq(&x->wait.lock);
3824        if (!x->done) {
3825                DECLARE_WAITQUEUE(wait, current);
3826
3827                wait.flags |= WQ_FLAG_EXCLUSIVE;
3828                __add_wait_queue_tail(&x->wait, &wait);
3829                do {
3830                        if (signal_pending(current)) {
3831                                timeout = -ERESTARTSYS;
3832                                __remove_wait_queue(&x->wait, &wait);
3833                                goto out;
3834                        }
3835                        __set_current_state(TASK_INTERRUPTIBLE);
3836                        spin_unlock_irq(&x->wait.lock);
3837                        timeout = schedule_timeout(timeout);
3838                        spin_lock_irq(&x->wait.lock);
3839                        if (!timeout) {
3840                                __remove_wait_queue(&x->wait, &wait);
3841                                goto out;
3842                        }
3843                } while (!x->done);
3844                __remove_wait_queue(&x->wait, &wait);
3845        }
3846        x->done--;
3847out:
3848        spin_unlock_irq(&x->wait.lock);
3849        return timeout;
3850}

可中断的并且可超时返回的等待完成量。

3、唤醒完成量:

(1)completion()函数:/linux/kernel/sched.c

3707void fastcall complete(struct completion *x)
3708{
3709        unsigned long flags;
3710
3711        spin_lock_irqsave(&x->wait.lock, flags);
3712        x->done++;
3713        __wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
3714                         1, 0, NULL);
3715        spin_unlock_irqrestore(&x->wait.lock, flags);
3716

用于唤醒一个等待该完成量的进程。

(2)completion_all()函数:/linux/kernel/sched.c

3719void fastcall complete_all(struct completion *x)
3720{
3721        unsigned long flags;
3722
3723        spin_lock_irqsave(&x->wait.lock, flags);
3724        x->done += UINT_MAX/2;
3725        __wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
3726                         0, 0, NULL);
3727        spin_unlock_irqrestore(&x->wait.lock, flags);
3728

唤醒所有等待给完成量的进程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值