Linux内核同步机制之completion

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

结构与初始化

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

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

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

静态初始化:

#define COMPLETION_INITIALIZER(work) \
{ 0, __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 = 0;
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 ?: 1;
}

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 ?: 1;
}

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

不看内核实现的源代码我们也能想到他的实现,不外乎在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=250;
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 0;
} 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<0)
return result;
if(complete_major==0)
complete_major=result;
return 0;
}
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 该命令用于写入该设备。

Linux内核同步机制之completion【转】的更多相关文章

  1. [内核同步]Linux内核同步机制之completion

    转自:http://blog.csdn.net/bullbat/article/details/7401688 内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束.这个活动 ...

  2. Linux内核同步机制之completion

    内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束.这个活动可能是,创建一个新的内核线程或者新的用户空间进程.对一个已有进程的某个请求,或者某种类型的硬件动作,等等.在这种 ...

  3. Linux内核同步机制

    http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...

  4. [内核同步]浅析Linux内核同步机制

    转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...

  5. 浅析Linux内核同步机制

    非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...

  6. Linux内核同步机制--转发自蜗窝科技

    Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...

  7. Linux内核同步机制之(五):Read Write spin lock【转】

    一.为何会有rw spin lock? 在有了强大的spin lock之后,为何还会有rw spin lock呢?无他,仅仅是为了增加内核的并发,从而增加性能而已.spin lock严格的限制只有一个 ...

  8. Linux内核同步机制之(四):spin lock【转】

    转自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享 ...

  9. Linux 内核同步机制

        本文将就自己对内核同步机制的一些简要理解,做出一份自己的总结文档.     Linux内部,为了提供对共享资源的互斥访问,提供了一系列的方法,下面简要的一一介绍. Technorati 标签: ...

随机推荐

  1. ElasticSearch 基础<转载>

    使用curl命令操作elasticsearch 大岩不灿 发表于 2015年4月25日 浏览 13,463 次 第一:_cat系列_cat系列提供了一系列查询elasticsearch集群状态的接口. ...

  2. MySQL Replication之主从切换

    在生产环境中,我们的架构很多都是一主多从.比如一个主数据库服务器M,两个从数据库服务器S1,S2同时指向主数据库服务器M.当主服务器M因为意外情况宕机,需要将其中的一个从数据库服务器(假设选择S1)切 ...

  3. 【JS】for in循环对象,hasOwnProperty()的作用

    var obj = { name:"echolun", age:", sex:"male" }, objName=[], //用来装对象属性名 obj ...

  4. WCF 学习总结1 -- 简单实例

    从VS2005推出WCF以来,WCF逐步取代了Remoting, WebService成为.NET上分布式程序的主要技术.WCF统一的模型整合了以往的 WebService.Remoting.MSMQ ...

  5. 第19课-数据库开发及ado.net ADO.NET--SQLDataReader使用.SqlProFiler演示.ADoNET连接池,参数化查询.SQLHelper .通过App.Config文件获得连接字符串

    第19课-数据库开发及ado.net ADO.NET--SQLDataReader使用.SqlProFiler演示.ADoNET连接池,参数化查询.SQLHelper .通过App.Config文件获 ...

  6. 【RabbitMQ】1、RabbitMQ的几种典型使用场景

    RabbitMQ主页:https://www.rabbitmq.com/ AMQP AMQP协议是一个高级抽象层消息通信协议,RabbitMQ是AMQP协议的实现.它主要包括以下组件: 1.Serve ...

  7. Graveyard(poj3154)

    Graveyard Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1289   Accepted: 660   Specia ...

  8. Jquery封装(学习)01

    1.在开发过程中,我们有时候会经常用到重复的jquey代码,最常见的是我们那里需要就再哪里复制粘贴,这样大大增加了冗余代码,维护起来也不方便.我们何不把共同的jquery代码封装起来,哪里需要就哪里调 ...

  9. 说说gogoTester-nodejs 的实现

    一直在使用goagent,所以对于查找google可用ip有了很大的兴趣,在github上面发现有一个项目是gogoTester,是用来查找google ip的,于是突发奇想自己用nodejs写了一个 ...

  10. js-权威指南学习笔记19

    第十九章 jQuery类库 1.传递HTML文本字符串给$()方法,jQuery会根据传入的文本创建好HTML元素并封装为jQuery对象返回. 2.想要遍历jQuery对象中的所有元素时,可以调用e ...