转自: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先释放了,那么,在完成量等待到来,接下来是什么样的流程?

[内核同步]Linux内核同步机制之completion的更多相关文章

  1. 【原创】解BUG-xenomai内核与linux内核时间子系统之间存在漂移

    版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 一.问题起源 何为漂移?举个例子两颗32.768kH ...

  2. Android内核和Linux内核的区别

    1.Android系统层面的底层是Linux,并且在中间加上了一个叫做Dalvik的Java虚拟机,从表面层看是Android运行库.每个Android应用都运行在自己的进程上,享有Dalvik虚拟机 ...

  3. 【内核】Linux内核Initrd机制解析,内核更新步骤,grub配置说明

    什么是Initrd initrd的英文含义是 boot loader initialized RAM disk,就是由boot loader初始化的内存盘.在 linux内核启动前, boot loa ...

  4. Linux内核分析——Linux内核学习总结

    马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核学习总结 一 ...

  5. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  6. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  7. 内核操作系统Linux内核变迁杂谈——感知市场的力量

    本篇文章个人在青岛游玩的时候突然想到的...今天就有想写几篇关于内核操作系统的博客,所以回家到以后就奋笔疾书的写出来发表了 Jack:什么是操作系统? 我:你买了一台笔记本,然后把整块硬盘彻底格式化, ...

  8. 《Linux内核--分析Linux内核创建一个新进程的过程 》 20135311傅冬菁

    20135311傅冬菁 分析Linux内核创建一个新进程的过程 一.学习内容 进程控制块——PCB  task_struct数据结构 PCB task_struct中包含: 进程状态.进程打开的文件. ...

  9. Linux内核分析-Linux内核如何装载和启动一个可执行程序

    ID:fuchen1994 实验要求: 理解编译链接的过程和ELF可执行文件格式,详细内容参考本周第一节: 编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态 ...

随机推荐

  1. Windows7系统下JAVA运行环境下载、安装和设置(第二次更新:2012年03月14日)

    1.下载 地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html,(由于Sun于2009年被oracle收购所以网址 ...

  2. Visual Studio 如何恢复默认设置

    我们在使用 Visual Studio 的时候,常常有一些窗口不见了又弄不回来的情况,如何恢复默认设置呢? 1.点击“开始-->程序-->Microsoft Visual Studio20 ...

  3. JVM实用参数——新生代垃圾回收

    JVM实用参数目录 JVM实用参数——新生代垃圾回收 概述 第1部分  新生代垃圾回收介绍 第2部分 参数介绍 参考 第1部分  新生代垃圾回收介绍 本部分,我们将关注堆(heap) 中一个主要区域, ...

  4. IIS7配置asp网站

    An error occurred on the server when processing the URL. Please contact the system administrator. If ...

  5. iOS开发Swift篇—(一)简单介绍

    iOS开发Swift篇—简单介绍 一.简介 Swift是苹果于2014年WWDC(苹果开发者大会)发布的全新编程语言 Swift在天朝译为“雨燕”,是它的LOGO 是一只燕子,跟Objective-C ...

  6. 【58测试】【贪心】【离散】【搜索】【LIS】【dp】

    第一题 大天使之剑 大意: 有n个怪,每个怪的ph 为 h[i],有三种攻击方式,普通攻击:一次打一个怪一滴血:重击(消耗1魔法值):一次打一个怪两滴血:群体攻击(消耗1魔法值):一次打所有怪一滴血. ...

  7. enmo_day_10

    RMAN 创建备份集 : backup as backupset format ‘/backup/df_%d_%s_%p/bus’ tablespace hr_data; 创建镜像副本 :(备份慢,恢 ...

  8. ubuntu13.04 nfs配置

    开发板和主机之间共享文件,常常需要配置nfs服务: 服务器端安装:root#:apt-get install nfs-common root#:apt-get install nfs-kernel-s ...

  9. Objective-C学习笔记-第三天(1)

    今天开始用oc写iOS程序,遇到的问题有 1.在不同的类使用类的方法或者访问类的属性的时候(公开的方法或者属性),方法或者属性必须在类头文件中声明. 2.对象类型的声明以及定义需要用*,表明这个是一个 ...

  10. 二叉搜索树 C++代码实现

    暂未发现什么bug,如果发现请指出. #include<iostream> using namespace std; //定义二叉搜索树的结点 struct Node { int data ...