文章目录

  • 一、总结
    • 完成量和(信号量&互斥量)的差异
  • 二、completion结构体
  • 三、初始化completion
    • 1、init_completion() 宏
    • 2、__init_completion()函数
  • 四、静态定义并初始化
    • 1、DECLARE_COMPLETION宏
      • COMPLETION_INITIALIZER宏
  • 五、completion休眠
    • 1、wait_for_completion()函数
    • 2、wait_for_completion_timeout()函数
    • 3、wait_for_completion_interruptible()函数
  • 六、complete唤醒
    • 1、complete()函数
    • 2、complete_all()函数
  • 七、例程
    • 1、驱动源文件
    • 2、测试app
    • 3、现象

一、总结

完成量用于进程/线程同步,与信号量/互斥量类似

完成量和(信号量&互斥量)的差异

信号量/互斥量:资源对所有进程/线程是公平的,按先来后到顺序使用

完成量:一个线程的运行依赖另一个线程,那么它们使用临界资源时不再是公平的,被依赖方先使用。

二、completion结构体

来表示完成量
include/linux/completion.h

struct completion {// 表示当前completion的状态unsigned int done;// 等待队列头wait_queue_head_t wait;
};

三、初始化completion

1、init_completion() 宏

include/linux/completion.h

// 注意x为 completion结构体指针
#define init_completion(x) __init_completion(x)
// 后面的是函数,定义见下

2、__init_completion()函数

include/linux/completion.h

static inline void __init_completion(struct completion *x)
{x->done = 0;// 初始化等待队列头init_waitqueue_head(&x->wait);
}

四、静态定义并初始化

1、DECLARE_COMPLETION宏

两个动作:定义,初始化

#define DECLARE_COMPLETION(work) \struct completion work = COMPLETION_INITIALIZER(work)

COMPLETION_INITIALIZER宏

#define COMPLETION_INITIALIZER(work) \{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }

五、completion休眠

1、wait_for_completion()函数

kernel/sched/completion.c
可能会使进程线程一直休眠
休眠时,进程线程不接收中断信号

// 完成量结构体指针
void __sched wait_for_completion(struct completion *x)

2、wait_for_completion_timeout()函数

kernel/sched/completion.c
限定了进程线程的休眠时间
休眠时,进程线程不接收中断信号

// x:完成量结构体指针
// timeout:超时时间
unsigned long __sched
wait_for_completion_timeout(struct completion *x, unsigned long timeout)

3、wait_for_completion_interruptible()函数

kernel/sched/completion.c
此函数引起进程线程休眠时,进程线程仍然能够响应外界发进来的中断信号

//  x:完成量结构体指针
int __sched wait_for_completion_interruptible(struct completion *x)

六、complete唤醒

1、complete()函数

kernel/sched/completion.c

//  x:完成量结构体指针
void complete(struct completion *x)

注意此函数只能唤醒一个进程或者线程,之前 wait_for_completion 可以在多个进程或者线程之间休眠。

2、complete_all()函数

kernel/sched/completion.c
唤醒所有在此完成量上休眠的进程或者线程

void complete_all(struct completion *x)

七、例程

1、驱动源文件

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/jiffies.h>
#include<linux/interrupt.h>
#include<linux/completion.h>
#include<linux/ide.h>#define DTSLED_NAME "dtsled"
#define LED_OFF 0
#define LED_ON 1// 定义一组全局变量,用于存放内存映射得到的虚拟地址
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
// 147:定义一个完成量
struct completion led_wait;
static unsigned int write_data;extern struct dtsled_dev dtsled;static void led_switch(u8 state)
{u32 val = 0;if(state == LED_ON){val = readl(GPIO1_DR);val &= (~(1<<3));writel(val, GPIO1_DR);}else if(state == LED_OFF){val = readl(GPIO1_DR);val |= (1<<3);writel(val, GPIO1_DR);}
}static int dtsled_open(struct inode *inode, struct file *filp)
{int ret;if(filp->f_flags & O_NONBLOCK){;}else{;}// struct file 结构体定义在 include/linux/fs.h 中// struct file 有一个成员(空指针):void *private_datafilp->private_data = &dtsled;return 0;
}static int dtsled_release(struct inode *inode, struct file *filp)
{//struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;return 0;
}static ssize_t dtsled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{int ret;u8 databuf[32] = {0};//struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;printk("%s(%d):buf=(%s), count=%d\n", __FILE__, __LINE__, buf, count);ret = copy_from_user(databuf, buf, count);printk("%s(%d):databuf=(%s)\n", __FILE__, __LINE__, databuf);if(ret < 0){printk("%s(%d):Kernel write failed.\r\n", __FILE__, __LINE__);return -1;}// s是输入字符串,base可以是10(10进制)或16(16进制),或者是0自动识别,res存放转换后的整形值// kstrtox.cret = kstrtoint(databuf, 16, &write_data);printk("%s(%d):write_data=%d", __FILE__, __LINE__, write_data);if(write_data){// 147:写入的是1,那么唤醒休眠在此完成量上的所有进程complete_all(&led_wait);printk("%s(%d):have woken up.\n", __FILE__, __LINE__);return count;}else{// 147:如果写入的是0,那么调用此函数的进程线程休眠在此完成量上wait_for_completion(&led_wait);led_switch(LED_OFF);printk("%s(%d):\n", __FILE__, __LINE__);}return 0;
}// 操作集结构体,是 struct cdev 的成员
static const struct file_operations dtsled_fops =
{.owner = THIS_MODULE,.write = dtsled_write,.open = dtsled_open,.release = dtsled_release,
};// 自定义结构体类型来描述 LED
struct dtsled_dev
{dev_t devid;struct cdev cdev; // 用于注册字符设备,操作集结构体是其成员变量struct file_operations dtsled_fops; // 操作集结构体struct class *class; // 用于自动创建设备节点struct device *device; // 用于自动创建设备节点u32 major;u32 minor;struct device_node *nd; // 设备树结点,用来表示 LED 设备节点
};// 自定结构体来描述 LED
struct dtsled_dev dtsled;// 驱动入口函数
static int __init dtsled_init(void)
{int ret = 0;int val = 0;const char *str;u32 regdata[10];u8 i;// 1、使用新方法来获取设备号dtsled.major = 0;if(dtsled.major){dtsled.devid = MKDEV(dtsled.major, 0);ret = register_chrdev_region(dtsled.devid, 1, DTSLED_NAME);}else{ret = alloc_chrdev_region(&dtsled.devid, 0, 1, DTSLED_NAME);dtsled.major = MAJOR(dtsled.devid);dtsled.minor = MINOR(dtsled.devid);}if(ret < 0){printk("%d:Register char dev error.\r\n", __LINE__);goto fail_devid;}// 2、添加(注册)字符设备dtsled.cdev.owner = THIS_MODULE;// zhu yi liang ge qu di zhi fu !cdev_init(&dtsled.cdev, &dtsled_fops);ret = cdev_add(&dtsled.cdev, dtsled.devid, 1);if(ret < 0){goto fail_cdev;}// 3、自动创建设备节点,需要一个 struct class,一个 struct devicedtsled.class = class_create(THIS_MODULE, DTSLED_NAME);if(IS_ERR(dtsled.class)){ret = PTR_ERR(dtsled.class);goto fail_class;}//  第一个 NULL 表示副设备,  第二个 NULL 表示 drvdatadtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if(IS_ERR(dtsled.device)){ret = PTR_ERR(dtsled.device);goto fail_device;}// 4、设备树相关,读取属性值dtsled.nd = of_find_node_by_path("/alpha_led");if(dtsled.nd == NULL){goto fail_findnd;}// 获取状态属性,注意是指针的指针ret = of_property_read_string(dtsled.nd, "status", &str);if(ret){printk("%d:fail\r\n", __LINE__);goto fail_rs;}else{printk("%d:status = \"%s\"\r\n", __LINE__, str);}// 获取节点属性值ret = of_property_read_string(dtsled.nd, "compatible", &str);if(ret){printk("%d:fail\r\n", __LINE__);goto fail_rs;}else{printk("%d:compatible = \"%s\"\r\n", __LINE__, str);}// 获取 reg 属性, 也就是获取 u32 类型属性值的方法ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);if(ret < 0){printk("%d:fail\r\n", __LINE__);goto fail_rs;}else{printk("< reg > = < ");for(i=0; i<10; i++){printk("%#X ", regdata[i]);}printk(">\r\n");}// 5、初始化 LEDIMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);GPIO1_DR = of_iomap(dtsled.nd, 3);GPIO1_GDIR = of_iomap(dtsled.nd, 4);// 初始化 LED 相关寄存器val = readl(IMX6U_CCM_CCGR1);val &= (~(3<<26));val |= (3<<26);writel(val, IMX6U_CCM_CCGR1);writel(0X5, SW_MUX_GPIO1_IO03);//io muxwritel(0X10B0, SW_PAD_GPIO1_IO03); // set electrical properityval = readl(GPIO1_GDIR); // set bit3, outval |= (1<<3); writel(val, GPIO1_GDIR);val = readl(GPIO1_DR);val &= (~(1<<3));//turn on led writel(val, GPIO1_DR);// 147:初始化 completion 结构体init_completion(&led_wait);return 0;fail_rs:
fail_findnd:device_destroy(dtsled.class, dtsled.devid);
fail_device:class_destroy(dtsled.class);
fail_class:cdev_del(&dtsled.cdev);
fail_cdev:unregister_chrdev_region(dtsled.devid, 1);
fail_devid:return ret;
}// 出口函数
static void __exit dtsled_exit(void)
{unsigned int val = 0;val = readl(GPIO1_DR);val |= (1<<3); // turn off ledwritel(val, GPIO1_DR);// 取消地址映射iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);// 删除字符设备cdev_del(&dtsled.cdev);// 释放设备号unregister_chrdev_region(dtsled.devid, 1);// 删除设备device_destroy(dtsled.class, dtsled.devid);// 删除类class_destroy(dtsled.class);
}// 注册出口函数和入口函数
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");

2、测试app

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>int main(int argc, char *argv[])
{int error;int fd;if(argc != 3){printf("%s(%d):command error!\n", __FILE__, __LINE__);return -1;}/*打开文件*/fd = open(argv[1], O_RDWR|O_NONBLOCK);if(fd < 0){printf("%s(%d):fail to open file : %s !!!\n", __FILE__, __LINE__, argv[1]);return -1;}error = write(fd,argv[2],strlen(argv[2]));if(error < 0){printf("%s(%d):write file error! \n", __FILE__, __LINE__);close(fd);}/*关闭文件*/error = close(fd);if(error < 0){printf("%s(%d):close file error! \n", __FILE__, __LINE__);}return 0;
}

3、现象

/lib/modules/4.1.15 # modprobe test.ko
211:status = "okay"
222:compatible = "alientek, alphaled"
< reg > = < 0X20C406C 0X4 0X20E0068 0X4 0X20E02F4 0X4 0X209C000 0X4 0X209C004 0X4 >
/lib/modules/4.1.15 # ./app /dev/dtsled 0 &
/lib/modules/4.1.15 # /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(86):buf=(0), count=1
/home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(88):databuf=(0)
/home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(97):write_data=0
/lib/modules/4.1.15 #
/lib/modules/4.1.15 #
/lib/modules/4.1.15 # echo 1 > /dev/dtsled /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(86):buf=(1
), count=2
/home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(88):databuf=(1
)
/home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(97):write_data=1
/home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(108):
/home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(101):have woken up.
[1]+  Done                       ./app /dev/dtsled 0
/lib/modules/4.1.15 #
/lib/modules/4.1.15 #

描述:
加载模块后,led亮。
app进程调用write,输入0, 进程休眠。
echo 1到设备,唤醒app进程。
app进程继续熄灯。

147 completion机制基本概念相关推荐

  1. java 熔断器模式_微服务架构熔断器机制的概念以及常用组件类型

    熔断器机制是我们在学习微服务编程开发的时候需要重点掌握的一个编程技术知识点,而今天我们就通过案例分析来了解一下,熔断器机制的概念以及常用组件类型都有哪些. 所谓熔断器机制,即类似电流的保险器,当然电压 ...

  2. 动画详解Transformer模型注意力机制的概念与模型搭建

    多头注意力机制 通过上一期的分享,我们了解了transformer模型中的多头注意力机制的概念,且通过7个attention注意力机制的变形,彻底了解了tranformer模型的多头注意力机制,哪里重 ...

  3. Java反射机制的基本概念与使用_Java进阶之reflection(反射机制)——反射概念与基础...

    反射机制是Java动态性之一,而说到动态性首先得了解动态语言.那么何为动态语言? 一.动态语言 动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化.比如常见 ...

  4. Nest.js模块机制的概念和实现原理

    原文链接: https://mp.weixin.qq.com/s/fQVPuoB7Lk88a_N4OQbPng 作者: 子慕大诗人 1 前言 Nest 提供了模块机制,通过在模块装饰器中定义提供者.导 ...

  5. Nestjs模块机制的概念和实现原理

    原文链接: https://mp.weixin.qq.com/s/fQVPuoB7Lk88a_N4OQbPng 作者: 子慕大诗人 1 前言 Nest 提供了模块机制,通过在模块装饰器中定义提供者.导 ...

  6. java反射机制的概念及原理

    java反射机制 什么是反射? 在java开发中有一个非常重要的概念就是java反射机制,也是java的重要特征之一.反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它 ...

  7. sql server 性能_SQL Server预读机制; 概念和性能提升

    sql server 性能 The user's read requests in SQL Server are managed and controlled by the SQL Server Re ...

  8. Java反射机制基本概念与相关Class类对反射机制的实现

    Java反射机制 1.何为反射 2.反射作用 3.Class类实例化应用 3.1.进行Class类对象的实例化的三种方式 3.2.Class类实例化其他类对象的两种方式 3.2.1.调用newInst ...

  9. OAF_EO系列2 - Validation数据校验验证机制(概念)

    2014-06-12 Created By BaoXinjian 一.摘要 1. 在Update数据之前,我们往往要对待更新的记录进行有效性的校验,校验级别包括 Attribute Level Val ...

最新文章

  1. c语言中浮点数和整数转换_C中的数据类型-整数,浮点数和空隙说明
  2. 转,大佬关于虚拟内存与物理内存关系讲解。
  3. PHP MVC框架核心类
  4. Cisco PIX防火墙配置命令大全
  5. Windows Print Spooler 远程代码执行漏洞(CVE-2021-34527)
  6. 构造函数与一般函数的区别
  7. java poi设置单元格格式为数值_java中导出excel设置单元格的样式为数字格式怎样设置?...
  8. (转)When Milliseconds Make Millions
  9. python 获取英文人名翻译
  10. 【科研】如何查看自己期刊是不是SCI/EI(含期刊各种信息查询)
  11. 软考(软件设计师)应该如何备考?
  12. 拼多多商品采集、商品数据解析详解
  13. 离散化-利用计算机求解y=x,离散信号处理(双语)-中国大学mooc-题库零氪
  14. matlab 转换为相对湿度
  15. 《Docker从入门到实践》
  16. 计算机专业的研究方向
  17. R语言的四种数据结构---向量
  18. 适合设计行业使用的电脑
  19. 一些数据采集卡使用过程中常见问题
  20. 字符串处理,输入N个学生的名字,按字母顺序输出

热门文章

  1. 居转户问题1-被退回之后附件如何删除?
  2. 1992年的鹿鼎记台词,嘛的,哥20年后才看到
  3. while it seems to fit format ‘yyyy-MM-dd‘T‘HH:mm:ss.SSSZ‘, parsing fails (leniency? null))
  4. 人员定位系统如何构筑化工企业安全生产防线
  5. 万年历c语言攀枝花,C语言实现万年历
  6. “三门问题”背后的概率论原理解析
  7. mysql根据身份证查询年龄,地址,性别
  8. Pyecharts 静态图片输出ppt中动态图表
  9. \0 的ASCII码值是多少
  10. 第55课:60分钟内从零起步驾驭Hive实战学习笔记