转自:http://blog.csdn.net/bullbat/article/details/7401688

内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束。这个活动可能是,创建一个新的内核线程或者新的用户空间进程、对一个已有进程的某个请求,或者某种类型的硬件动作,等等。在这种情况下,我们可以使用信号量来同步这两个任务。然而,内核中提供了另外一种机制——completion接口。Completion是一种轻量级的机制,他允许一个线程告诉另一个线程某个工作已经完成。

理解同步completion的例子

这是一个公交司机和售票员之间的线程调度,用于理解完成量,完成量是对信号量的一种补充,主要用于多处理器系统上发生的一种微妙竞争。在这里两个线程间同步,只有当售票员把门关了后,司机才能开动车,只有当司机停车后,售票员才能开门。

线程(进程)之间的同步大多使用completion,而互斥资源的保护大多使用信号量(互斥锁or自旋锁)。

结构与初始化

Completion在内核中的实现基于等待队列(关于等待队列理论知识在前面的文章中有介绍),completion结构很简单:

 struct completion {
unsigned int done;/*用于同步的原子量*/
wait_queue_head_t wait;/*等待事件队列*/
};

和信号量一样,初始化分为静态初始化和动态初始化两种情况:

静态初始化:

 #define COMPLETION_INITIALIZER(work) \
{ , __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) } #define DECLARE_COMPLETION(work) \
struct completion work = COMPLETION_INITIALIZER(work)

动态初始化:

 static inline void init_completion(struct completion *x)
{
x->done = ;
init_waitqueue_head(&x->wait);
}

可见,两种初始化都将用于同步的done原子量置位了0,后面我们会看到,该变量在wait相关函数中减一,在complete系列函数中加一。

实现

同步函数一般都成对出现,completion也不例外,我们看看最基本的两个complete和wait_for_completion函数的实现。

wait_for_completion最终由下面函数实现:

 static inline long __sched
do_wait_for_common(struct completion *x, long timeout, int state)
{
if (!x->done) {
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 ?: ;
}

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, , , NULL);
spin_unlock_irqrestore(&x->wait.lock, flags);
}  

不看内核实现的源代码我们也能想到他的实现,不外乎在wait函数中循环等待done变为可用(正),而另一边的complete函数为唤醒函数,当然是将done加一,唤醒待处理的函数。是的,从上面的代码看到,和我们想的一样。内核也是这样做的。

运用

运用LDD3中的例子:

 #include <linux/module.h>
#include <linux/init.h> #include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/completion.h> MODULE_LICENSE("GPL"); static int complete_major=;
DECLARE_COMPLETION(comp); ssize_t complete_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)
{
printk(KERN_ERR "process %i (%s) going to sleep\n",current->pid,current->comm);
wait_for_completion(&comp);
printk(KERN_ERR "awoken %i (%s)\n",current->pid,current->comm);
return ;
} ssize_t complete_write(struct file *filp,const char __user *buf,size_t count,loff_t *pos)
{
printk(KERN_ERR "process %i (%s) awakening the readers...\n",current->pid,current->comm);
complete(&comp);
return count;
} struct file_operations complete_fops={
.owner=THIS_MODULE,
.read=complete_read,
.write=complete_write,
}; int complete_init(void)
{
int result;
result=register_chrdev(complete_major,"complete",&complete_fops);
if(result<)
return result;
if(complete_major==)
complete_major=result;
return ;
}
void complete_cleanup(void)
{
unregister_chrdev(complete_major,"complete");
}
module_init(complete_init);
module_exit(complete_cleanup);

测试步骤

1, mknod /dev/complete创建complete节点,在linux上驱动程序需要手动创建文件节点。

2, insmod complete.ko 插入驱动模块,这里要注意的是,因为我们的代码中是手动分配的设备号,很可能被系统已经使用了,所以如果出现这种情况,查看/proc/devices文件。找一个没有被使用的设备号。

3, cat /dev/complete 用于读该设备,调用设备的读函数

4, 打开另一个终端输入 echo “hello” > /dev/complete 该命令用于写入该设备。

问题

1. 如果完成量还没有处于wait状态,complete先释放了,那么,在完成量等待到来,接下来是什么样的流程?

最新文章

  1. Web导出EXCEL
  2. 如何提高nodejs程序的稳定性,健壮性
  3. javascript之with的使用 弊端
  4. 基于矩阵模式的 Web 软件测试手段(转)
  5. 机器学习 —— 概率图模型(CPD)
  6. [原]Unity3D深入浅出 - 认识开发环境中的GameObject菜单栏
  7. OpenCV 实现图片的水平投影与垂直投影,并进行行分割
  8. 2018-2019-2 网络对抗技术 20165237 Exp6 信息搜集与漏洞扫描
  9. VB控件间的拖放
  10. Python中的 @staticmethod@classmethod方法
  11. Flask图书管管理表
  12. VS2017中VC++项目添加StringTable资源
  13. IDEA去除自动检测bean是否存在
  14. Python下HttpHTTPClient和AsyncHTTPClient
  15. mysql5.7 误删管理员root账户
  16. 21-[模块]-configparser
  17. JFinal源码详解
  18. [CODE FESTIVAL 2017]Full Tournament
  19. java基础09 数组的使用
  20. PHP基础面试题(1-10)

热门文章

  1. linux性能测试命令-----top
  2. MySQL数据库管理用户权限
  3. jQuery实现滚动效果详解1
  4. Linux服务器数据库的导入和导出
  5. JavaEE 7技术一览
  6. 收藏的牛人的Backbone分享教程
  7. 【转】解决编译安装NGINX时make报错
  8. C语言编译链接
  9. Jmeter—6 CSV Data Set Config 通过文件导入数据
  10. flexslider