第五章 completion 和锁

   上一回主要介绍了使用信号量好处理竞态问题。有时候,驱动会碰见这种问题,就是一个线程在运行时,需要等待另外一个线程完成某个动作才能继续。这种情况跟竞态还是有点区别的,说不太清楚。当然这种情形可以用信号量来达到效果,但是这种方法有很多的缺点(对于这种特殊的情况来说)。于是内核就提供了一个全新的接口来专门处理这种情况。

  Completion(定义在<linux/completion.h>),正如它的名字,它的作用在于让一个线程告诉另一个线程它完成了某项工作。

跟信号量得使用一样,它可以通俗的理解为三个主要部分:初始化,申请和释放。

初始化的代码:

1 DECLARE_COMPLETION(my_completion);//全局静态
2 struct completion my_completion;
3 /* ... */
4 init_completion(&my_completion);//动态

  下面的代码用于告知内核,当前线程需要等待某项工作的完成。

1 void wait_for_completion(struct completion *c);

  下面的代码用于通知之前线程工作已完成。

1 void complete(struct completion *c);
2 void complete_all(struct completion *c);//这个用于通知所有

  完整的例子如下:

 1 DECLARE_COMPLETION(comp);
2 ssize_t complete_read (struct file *filp, char __user *buf, size_t count, loff_t
3 *pos)
4 {
5 printk(KERN_DEBUG "process %i (%s) going to sleep\n",
6 current->pid, current->comm);
7 wait_for_completion(&comp);
8 printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
9 return 0; /* EOF */
10 }
11 ssize_t complete_write (struct file *filp, const char __user *buf, size_t count,
12 loff_t *pos)
13 {
14 printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
15 current->pid, current->comm);
16 complete(&comp);
17 return count; /* succeed, to avoid retrial */
18 }

  这个代码的功能在于每当一个有读操作时,就挂起,直到有一个写操作被执行,就唤醒一个读操作(其实这个代码在实际当中是没有任何价值的,作者应该是单纯的为了举例子吧)。

   Completion和信号量的区别还是很大的,虽然两者的使用看起来实在很想:都是先初始化,然后在一个地方申请,在另外一个地方释放。但是信号量在申请的是资源,得到就继续,得不到就退出。Completion是申请一个完成动作,当申请一个Completion时,它当时是肯定不会得到结果的,但是它不会退出,而是在原处等待,直到收到动作已完成的通知,就继续执行之后的代码。

    事实上,在实际编程当中,处理竞态问题的最常用方法是使用自旋锁。自旋锁的使用很简单,还不就是初始化,关锁,开锁什么的,这个跟信号量的使用和completion的使用很类似。

初始化一个自旋锁:

1 spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
2 void spin_lock_init(spinlock_t *lock);//动态

  申请进入临界区(如果成功就把锁锁上,失败就等待):

1 void spin_lock(spinlock_t *lock);

  释放共享资源,同时开锁以供其他线程使用:

1 void spin_unlock(spinlock_t *lock);

  自旋锁和信号量的区别在于,当得不到资源时,信号量就会返回一个负数,通常的处理就是退出程序了;但是自旋锁在这种情况下会自旋,也就是不断的检查这个锁,直到它可用为止。由于这个原因,自旋锁能够适用于更多的情形,但是使用起来是很有风险的。随便想想就知道,“死锁”是很容易就产生的。

   在使用自旋锁时,在获取到锁之后的代码是一定要慎重处理的,你始终要记得,在你执行这段代码时,有着不知数目的线程在身后等待着你来开锁。首当其冲的,这段代码(临界区)必须不能休眠。在身后有那么多人在等你的情形下,你一定要尽快的完成工作并开锁。如果临界区进入休眠或者被中断,那么那些自旋中的线程就很悲剧了。临界区的代码的运行将直接影响自旋的线程。坏一点的情况,那些自旋中的线程可能会被你坑死。(就好像一堆人在等着上厕所,而厕所里头的人却在打电话,电话不打完就不出来,外面的人不知这一情形,还在苦苦等待.......)。然后,在不被打断的情形下,临界区的代码必须尽快的执行完毕,理由也是显而易见的。另外,使用自旋锁时一些设计上的失误可能是致命的。比如在临界区再次申请自旋锁,额   不用说了,后果肯定很严重的......

    自旋锁这东西还是很强大很复杂的,以后在实际使用中有什么新的体会在拿出来分享。

转载于:https://www.cnblogs.com/shadox/archive/2011/08/08/2131324.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值