Linux内核同步机制之completion【转】
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);
测试步骤:
- mknod /dev/complete创建complete节点,在linux上驱动程序需要手动创建文件节点。
- insmod complete.ko 插入驱动模块,这里要注意的是,因为我们的代码中是手动分配的设备号,很可能被系统已经使用了,所以如果出现这种情况,查看/proc/devices文件。找一个没有被使用的设备号。
- cat /dev/complete 用于读该设备,调用设备的读函数
- 打开另一个终端输入 echo “hello” > /dev/complete 该命令用于写入该设备。
Linux内核同步机制之completion【转】的更多相关文章
- [内核同步]Linux内核同步机制之completion
转自:http://blog.csdn.net/bullbat/article/details/7401688 内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束.这个活动 ...
- Linux内核同步机制之completion
内核编程中常见的一种模式是,在当前线程之外初始化某个活动,然后等待该活动的结束.这个活动可能是,创建一个新的内核线程或者新的用户空间进程.对一个已有进程的某个请求,或者某种类型的硬件动作,等等.在这种 ...
- Linux内核同步机制
http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...
- [内核同步]浅析Linux内核同步机制
转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...
- 浅析Linux内核同步机制
非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...
- Linux内核同步机制--转发自蜗窝科技
Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...
- Linux内核同步机制之(五):Read Write spin lock【转】
一.为何会有rw spin lock? 在有了强大的spin lock之后,为何还会有rw spin lock呢?无他,仅仅是为了增加内核的并发,从而增加性能而已.spin lock严格的限制只有一个 ...
- Linux内核同步机制之(四):spin lock【转】
转自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享 ...
- Linux 内核同步机制
本文将就自己对内核同步机制的一些简要理解,做出一份自己的总结文档. Linux内部,为了提供对共享资源的互斥访问,提供了一系列的方法,下面简要的一一介绍. Technorati 标签: ...
随机推荐
- 每一行代码都有记录—如何用git一步步探索项目的历史
每一行代码都有一块被隐藏了的文档信息. 下面的代码片段不管是谁写的,其第4行因为某些原因要访问一个DOM结点的clientLeft属性,但却对结果不作任何处理.这十分的莫名其妙,你能告诉我他们为什么要 ...
- Centos7下使用yum安装lnmp zabbix3.2
1:配置epel-release mysql zabbix 源 配置epel源 wget http://mirrors.aliyun.com/epel/epel-release-latest-7.no ...
- 解决使用vue-cli生成项目后项目地址自动添加#号的问题
vue-router官网https://router.vuejs.org/zh/guide/ vue的路由在默认的hash模式下,url会带有一个#,不美观,而且在微信分享,授权登录等都会有一些坑.所 ...
- WIN32控件使用系统样式
定义如下即可: #ifdef _M_IX86 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Mi ...
- Ubuntu下opencv的安装及IDEA开发配置
Ubuntu下opencv的安装及IDEA开发配置 环境配置 这篇博客主要介绍Ubuntu下opencv的安装编译,以及IDEA对opencv进行Java开发的环境配置 安装opencv 首先安装o ...
- office online server部署和简单操作
office online server是Office Web Apps Server的升级版本,安装环境必须为 Windows Server 2012 R2 参考地址:https://technet ...
- js 前端分页空间控件
现在web注重用户体验与交互性,ajax 提交数据的方式很早就流行了,它能够在不刷新网页的情况下局部刷新数据.前端分页多是用ajax请求数据(其他方式也有比如手动构造表单模拟提交事件等).通过js将查 ...
- [Redis] redis在centos下安装测试
下载软件,使用命令wget xxx,参数:url 例如: wget http://download.redis.io/releases/redis-3.0.0.tar.gz 解压缩,使用命令tar,参 ...
- Java基础——iO(三)
一.管道流 演示:PipedInputStream , PipedOutputStream 注意:管道流本身就不建议在一个线程中使用,这是因为向输出流中写的数据,都会存到输入流内部的一个1024字节 ...
- Redis——基础数据结构
Redis提供了5种基础数据结构,分别是String,list,set,hash和zset. 1.String Redis所有的键都是String.Redis的String是动态字符串,内部结构类似J ...