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

内核提供了完成量(completion)来完成上述需求;完成量是一个轻量级的机制,它允许一个线程告诉另一个线程某个工作已经完成;为了使用完成量,代码需要包含<linux/completion.h>;可以利用下面的宏静态的创建和初始化完成量;

 #define DECLARE_COMPLETION(work)

或者使用下面的方法动态的创建和初始化完成量;

 struct completion my_completion;
/* 初始化函数 */
static inline void init_completion(struct completion *x)

需要等待完成,可以调用下面的方法,这些方法都以wait_for_completion开头,区别在于比如是否可以打断,是否提供超时等;

 extern void wait_for_completion(struct completion *);
extern void wait_for_completion_io(struct completion *);
extern int wait_for_completion_interruptible(struct completion *x);
extern int wait_for_completion_killable(struct completion *x);
extern unsigned long wait_for_completion_timeout(struct completion *x,
unsigned long timeout);
extern unsigned long wait_for_completion_io_timeout(struct completion *x,
unsigned long timeout);
extern long wait_for_completion_interruptible_timeout(
struct completion *x, unsigned long timeout);
extern long wait_for_completion_killable_timeout(
struct completion *x, unsigned long timeout);
extern bool try_wait_for_completion(struct completion *x);

实际的完成事件触发则通过调用下面函数之一来完成;

 extern void complete(struct completion *);
extern void complete_all(struct completion *);

这两个函数在是否有多个线程在等待相同的完成事件上有所不同,complete只会唤醒一个等待线程,而complete_all允许唤醒所有等待线程;大多数情况下,只会有一个等待者,因此这两个函数产生相同的结果;一个完成量通常是一个单次设备,也就是说,它只会被使用一次后就被丢弃;但是,完成量结构也可以重复使用,如果没有使用complete_all,则我们可以重复使用一个完成量结构,只要那个将要触发的事件是明确的,就不会有问题;但是如果使用了complete_all,则必须在重新使用该结构之前重新对它进行初始化;下面函数用来快速进行重新初始化;

 static inline void reinit_completion(struct completion *x)

完成量的典型使用是在模块退出时的内核线程终止;在这种原型中,某些驱动程序的内部工作由一个内核线程在while (1)循环中完成,当内核准备清除该模块时,exit函数会告诉该线程退出并等待完成量;为了实现这个目的,内核包含了可用于这种线程的一个特殊函数;

 void complete_and_exit(struct completion *comp, long code)

比如内核中下面代码就说明这种场景:

 static int ldlm_pools_thread_main(void *arg)
{
struct ptlrpc_thread *thread = (struct ptlrpc_thread *)arg;
int c_time; thread_set_flags(thread, SVC_RUNNING);
wake_up(&thread->t_ctl_waitq); CDEBUG(D_DLMTRACE, "%s: pool thread starting, process %d\n",
"ldlm_poold", current_pid()); while () {
struct l_wait_info lwi; /*
* Recal all pools on this tick.
*/
c_time = ldlm_pools_recalc(LDLM_NAMESPACE_CLIENT); /*
* Wait until the next check time, or until we're
* stopped.
*/
lwi = LWI_TIMEOUT(cfs_time_seconds(c_time),
NULL, NULL);
l_wait_event(thread->t_ctl_waitq,
thread_is_stopping(thread) ||
thread_is_event(thread),
&lwi); if (thread_test_and_clear_flags(thread, SVC_STOPPING))
break;
thread_test_and_clear_flags(thread, SVC_EVENT);
} thread_set_flags(thread, SVC_STOPPED);
wake_up(&thread->t_ctl_waitq); CDEBUG(D_DLMTRACE, "%s: pool thread exiting, process %d\n",
"ldlm_poold", current_pid()); <strong>complete_and_exit(&ldlm_pools_comp, );</strong>
}
 static void ldlm_pools_thread_stop(void)
{
if (!ldlm_pools_thread)
return; thread_set_flags(ldlm_pools_thread, SVC_STOPPING);
wake_up(&ldlm_pools_thread->t_ctl_waitq); /*
* Make sure that pools thread is finished before freeing @thread.
* This fixes possible race and oops due to accessing freed memory
* in pools thread.
*/
<strong>wait_for_completion(&ldlm_pools_comp);</strong>
kfree(ldlm_pools_thread);
ldlm_pools_thread = NULL;
}

Linux设备驱动程序 之 完成量的更多相关文章

  1. Linux设备驱动程序 第三版 读书笔记(一)

    Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...

  2. Linux设备驱动程序学习之分配内存

    内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...

  3. linux设备驱动程序--在用户空间注册文件接口

    linux字符设备驱动程序--创建设备节点 基于4.14内核,运行在beagleBone green 在上一讲中,我们写了第一个linux设备驱动程序--hello_world,在驱动程序中,我们什么 ...

  4. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  5. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  6. 教你写Linux设备驱动程序:一个简短的教程

    教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html

  7. linux设备驱动程序_hello word 模块编译各种问题集锦

    在看楼经典书籍<linux设备驱动程序>后,第一个程序就是编写一个hello word 模块. 原以为非常easy,真正弄起来,发现问题不少啊.前两天编过一次,因为没有记录,今天看的时候又 ...

  8. Linux设备驱动程序学习----1.设备驱动程序简介

    设备驱动程序简介 更多内容请参考Linux设备驱动程序学习----目录 1. 简介   Linux系统的优点是,系统内部实现细节对所有人都是公开的.Linux内核由大量复杂的代码组成,设备驱动程序可以 ...

  9. Linux设备驱动程序学习----2.内核模块与应用程序的对比

    内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...

随机推荐

  1. javaIO——概述

    以前看java书,都将IO作为一个大的章节甚至模块来编写,可见IO在java语言中的重要性. java的流按功能和处理的目标数据不同可以分为字节流和字符流.字符流处理的基本数据单元是字符:字节流处理的 ...

  2. Windows 软件使用

    1.CMD 1. 查看端口对应进程 netstat -ano|findstr "443" 2.通过PID 查找对应进程 tasklist|findstr “<PID号> ...

  3. 项目构建工具之gradle

    groovy的高级特性: 可选的类型定义 def.assert.括号是可选的.字符串 .集合API.闭包: 构建脚本 项目project : group name version apply depe ...

  4. 【Log4J】

    学习mybatis中用到了Log4J 在此记录下 引入 引入Maven配置 <!-- https://mvnrepository.com/artifact/log4j/log4j --> ...

  5. 【TCP】连接管理

    TCP连接管理   本节将介绍一条TCP连接是如何建立和拆除的.此处假设客户机A上面的一个进程想要和服务 器B上的一个进程建立一条TCP连接.本文前面介绍的是比较正常的连接和拆除,特殊的会在后面介绍. ...

  6. (6)python基础数据类型

    python的六大标准数据类型 (一)Number   数字类型(int float bool complex) (二)String 字符串类型 (三)List 列表类型 (四)Tuple 元组类型 ...

  7. [Abp vNext微服务实践] - vue-element-admin登录二

    简介: Vue Element Admin是基于vue.element ui开发的后台管理ui,abp vNext是abp新一代微服务框架.本篇将会介绍如何改造Vue Element Admin权限验 ...

  8. Appium Desired Capabilities-iOS Only

    Appium Desired Capabilities-iOS Only These Capabilities are available only on the XCUITest Driver an ...

  9. 利用Struts上传文件

    在利用struts2完成上传文件到服务器时,遇到获取不到文件名 原因是在Action中的属性名没有和jsp中的属性名匹配 <%@ page language="java" i ...

  10. C# WebBrowser使用 网络(二)

    WebBrowser 简单操作 Form 代码 public partial class Form1 : Form { public Form1() { InitializeComponent(); ...