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

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

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

结构与初始化

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

[cpp] view plain copy

print?

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

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

静态初始化:

[cpp] view plain copy

print?

  1. #define COMPLETION_INITIALIZER(work) \
  2. { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
  3. #define DECLARE_COMPLETION(work) \
  4. struct completion work = COMPLETION_INITIALIZER(work)

动态初始化:

[cpp] view plain copy

print?

  1. static inline void init_completion(struct completion *x)
  2. {
  3. x->done = 0;
  4. init_waitqueue_head(&x->wait);
  5. }

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

实现

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

wait_for_completion最终由下面函数实现:

[cpp] view plain copy

print?

  1. static inline long __sched
  2. do_wait_for_common(struct completion *x, long timeout, int state)
  3. {
  4. if (!x->done) {
  5. DECLARE_WAITQUEUE(wait, current);
  6. wait.flags |= WQ_FLAG_EXCLUSIVE;
  7. __add_wait_queue_tail(&x->wait, &wait);
  8. do {
  9. if (signal_pending_state(state, current)) {
  10. timeout = -ERESTARTSYS;
  11. break;
  12. }
  13. __set_current_state(state);
  14. spin_unlock_irq(&x->wait.lock);
  15. timeout = schedule_timeout(timeout);
  16. spin_lock_irq(&x->wait.lock);
  17. } while (!x->done && timeout);
  18. __remove_wait_queue(&x->wait, &wait);
  19. if (!x->done)
  20. return timeout;
  21. }
  22. x->done--;
  23. return timeout ?: 1;
  24. }

而complete实现如下:

[cpp] view plain copy

print?

  1. void complete(struct completion *x)
  2. {
  3. unsigned long flags;
  4. spin_lock_irqsave(&x->wait.lock, flags);
  5. x->done++;
  6. __wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL);
  7. spin_unlock_irqrestore(&x->wait.lock, flags);
  8. }

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

运用

运用LDD3中的例子:

[cpp] view plain copy

print?

  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/sched.h>
  4. #include <linux/kernel.h>
  5. #include <linux/fs.h>
  6. #include <linux/types.h>
  7. #include <linux/completion.h>
  8. MODULE_LICENSE("GPL");
  9. static int complete_major=250;
  10. DECLARE_COMPLETION(comp);
  11. ssize_t complete_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)
  12. {
  13. printk(KERN_ERR "process %i (%s) going to sleep\n",current->pid,current->comm);
  14. wait_for_completion(&comp);
  15. printk(KERN_ERR "awoken %i (%s)\n",current->pid,current->comm);
  16. return 0;
  17. }
  18. ssize_t complete_write(struct file *filp,const char __user *buf,size_t count,loff_t *pos)
  19. {
  20. printk(KERN_ERR "process %i (%s) awakening the readers...\n",current->pid,current->comm);
  21. complete(&comp);
  22. return count;
  23. }
  24. struct file_operations complete_fops={
  25. .owner=THIS_MODULE,
  26. .read=complete_read,
  27. .write=complete_write,
  28. };
  29. int complete_init(void)
  30. {
  31. int result;
  32. result=register_chrdev(complete_major,"complete",&complete_fops);
  33. if(result<0)
  34. return result;
  35. if(complete_major==0)
  36. complete_major=result;
  37. return 0;
  38. }
  39. void complete_cleanup(void)
  40. {
  41. unregister_chrdev(complete_major,"complete");
  42. }
  43. module_init(complete_init);
  44. 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 该命令用于写入该设备。

时间: 2024-10-26 07:04:43

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

linux内核---嵌入式linux内核的五个子系统

转自:https://blog.csdn.net/qq_27522735/article/details/63251168 Linux内核主要由进程调度(SCHED).内存管理(MM).虚拟文件系统(VFS).网络接口(NET)和进程间通信(IPC)5个子系统组成,如图1所示. 图1 Linux内核的组成部分与关系 1.进程调度 进程调度控制系统中的多个进程对CPU的访问,使得多个进程能在CPU中"微观串行,宏观并行"地执行.进程调度处于系统的中心位置,内核中其他的子系统都依赖它,因为

安卓驱动开发(四)----安卓内核与linux内核的安装配置

安卓中的源代码包括安卓系统中的应用程序的源代码,SDK带的各种工具的源代码,NDK的源代码以及HAL源代码. 安卓源代码可以全部下载,也可以下载一部分.之后要对安卓的源代码进行编译,在编译后,会在安卓源代码根目录下生成一个out目录,所有编译的目标文件都在这个目录中,在out目录中有两个直接子目录,分别是host和target.前者表示在主机(x86)生成的工具,后者表示目标机(模认为ARMv5)运行的内容.在target中,包含两个直接子目录,分别是common和product.common包

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

20135311傅冬菁 分析Linux内核创建一个新进程的过程 一.学习内容 进程控制块——PCB  task_struct数据结构 PCB task_struct中包含: 进程状态.进程打开的文件.进程优先级信息 操作系统管理的三个功能: 1.进程管理 2.内存管理 3.文件系统 Linux进程的状态: 进程状态分析: long state是进程的运行状态,-1是未执行,0是执行中,大于0则是暂停: *stack 是建立一个内核堆栈: flags 是定义了每个进程的标识符: list_head

大话Linux内核中锁机制之RCU、大内核锁

大话Linux内核中锁机制之RCU.大内核锁 在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linux内核中锁机制>系列博文进行了总结,并提出关于目前Linux内核中提供的锁机制的一些基本使用观点. 十.RCU机制 本节将讨论另一种重要锁机制:RCU锁机制.首先我们从概念上理解下什么叫RCU,其中读(Read):读者不需要获得任何锁就可访问RCU保护的临界

linux 内核移植和根文件系统的制作

1.1 Linux内核基础知识 在动手进行Linux内核移植之前,非常有必要对Linux内核进行一定的了解,下面从Linux内核的版本和分类说起. 1.1.1  Linux版本 Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如2.6.29.1内核的Makefile中: VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 29 EXTRAVERSION = .1 其 中的“VERSION”和“PATCHLEVEL”组成主版本号,比如2.4.2.5

Linux内核工程导论——进程

进程 进程调度 概要 linux是个多进程的环境,不但用户空间可以有多个进程,而且内核内部也可以有内核进程.linux内核中线程与进程没有区别,因此叫线程和进程都是一样的.调度器调度的是CPU资源,按照特定的规则分配给特定的进程.然后占有CPU资源的资源去申请或使用硬件或资源.因此这里面涉及到的几个问题: 对于调度器来说: l  调度程序在运行时,如何确定哪一个程序将被调度来使用CPU资源? n  如何不让任何一个进程饥饿? n  如何更快的定位和响应交互式进程? l  单个CPU只有一个流水线

Linux内核线程kernel thread详解--Linux进程的管理与调度(十)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-02 Linux-4.5 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度-之-进程的描述 内核线程 为什么需要内核线程 Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求). 内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的. 内核线程就是内核的分身,一个分身可以处理一件特定事情.内核线程的调度由内核负责,一个内核线程处于阻

linux内核设计与实现一书阅读整理 之第一二章整合

第一章:Linux内核简介 一.Unix和linux Unix是一个强大.健壮和稳定的操作系统. 1.Unix内核特点 十分简洁:仅提供几百个系统调用并且有明确的目的: 在Unix中,大部分东西都被(或者正致力于)被当做文件对待: Unix内核即相关系统工具软件都是用C语言编写的,这使得系统有着强大的可移植性: Unix进程创建非常迅速,目标在于一次执行保质保量地完成一个任务 2.Linux与Unix异同 Linux是基于Unix的类系统,比如它也实现了Unix的API: 但它不同于Unix,没

《Linux内核设计与实现》读书笔记 1&amp;2

第一章    Linux内核简介 1.1Unix历史 Unix特点:1.很简洁 2.所有东西都被当成文件对待 3.Unix内核和相关的系统工具软件都是用C语言编写而成 4.进程创建非常迅速 所以Unix很强大. 1.2追寻Linus足迹:linux简介 Linus开发.Linux是类Unix系统.Linux内核也是自由软件. 1.3操作系统和内核简介 操作系统:在整个系统中负责完成最基本功能和系统管理的那些部分.包括内核.设备驱动程序.启动引导程序.命令行shell或者其他种类的用户界面.基本的