Linux设备驱动workqueue(工作队列)案例实现

简介: Linux设备驱动workqueue(工作队列)案例实现

一、Linux工作队列与Linux小任务机制的区别


   工作队列(work queue)是另外一种将工作推后执行的形式,tasklet(小任务机制)有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。      

   那么,什么情况下使用工作队列,什么情况下使用tasklet呢?如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet。    

   一般,不要轻易的去使用工作队列,因为每当创建一条工作队列,内核就会为这条工作队列创建一条内核线程。工作队列位于进程上下文,与软中断,tasklet有所区别,工作队列里允许延时,睡眠操作,而软中断,tasklet位于中断上下文,不允许睡眠和延时操作。


二、使用Linux工作队列


1、需要包含的头文件

#include <linux/workqueue.h>

2、工作队列相关的数据结构(各个版本内核可能不同,这里用的是3.5)

 1//工作队列结构
 2struct work_struct {
 3    atomic_long_t data;
 4    //链表处理
 5    struct list_head entry;
 6    //工作处理函数
 7    work_func_t func;
 8#ifdef CONFIG_LOCKDEP
 9    struct lockdep_map lockdep_map;
10#endif
11};

3、操作工作队列相关的API

 1创建一个队列就会有一个内核线程,一般不要轻易创建队列
 2位于进程上下文--->可以睡眠
 3定义:
 4    struct work_struct work;
 5
 6初始化:
 7    INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work));
 8
 9定义并初始化:
10    DECLARE_WORK(name, void (*func)(struct work_struct *work));
11
12===========================================================
13
14调度:
15    int schedule_work(struct work_struct *work);
16    返回1成功, 0已经添加在队列上
17
18延迟调度:
19    int schedule_delayed_work(struct work_struct *work, unsigned long delay);
20
21===========================================================
22
23创建新队列和新工作者线程:
24    struct workqueue_struct *create_workqueue(const char *name);
25
26调度指定队列:
27    int queue_work(struct workqueue_struct *wq, struct work_struct *work);
28
29延迟调度指定队列:
30    int queue_delayed_work(struct workqueue_struct *wq, 
31            struct work_struct *work, unsigned long delay);
32销毁队列:
33    void destroy_workqueue(struct workqueue_struct *wq);

4、Demo实现(基于Tiny4412 Linux3.5内核)

1#include <linux/module.h>
 2#include <linux/kernel.h>
 3#include <linux/init.h>
 4#include <linux/platform_device.h>
 5#include <linux/fb.h>
 6#include <linux/backlight.h>
 7#include <linux/err.h>
 8#include <linux/pwm.h>
 9#include <linux/slab.h>
10#include <linux/miscdevice.h>
11#include <linux/delay.h>
12#include <linux/gpio.h>
13#include <mach/gpio.h>
14#include <plat/gpio-cfg.h>
15#include <linux/timer.h>  /*timer*/
16#include <asm/uaccess.h>  /*jiffies*/
17#include <linux/delay.h>
18#include <linux/interrupt.h>
19#include <linux/workqueue.h>
20struct tasklet_struct task_t ; 
21struct workqueue_struct *mywork ;
22//定义一个工作队列结构体
23struct work_struct work;
24static void task_fuc(unsigned long data)
25{
26    if(in_interrupt()){
27             printk("%s in interrupt handle!\n",__FUNCTION__);
28        }
29}
30//工作队列处理函数
31static void mywork_fuc(struct work_struct *work)
32{
33    if(in_interrupt()){
34             printk("%s in interrupt handle!\n",__FUNCTION__);
35        }
36    msleep(2);
37    printk("%s in process handle!\n",__FUNCTION__);
38}
39
40static irqreturn_t irq_fuction(int irq, void *dev_id)
41{    
42    tasklet_schedule(&task_t);
43    //调度工作
44    schedule_work(&work);
45    if(in_interrupt()){
46         printk("%s in interrupt handle!\n",__FUNCTION__);
47    }
48    printk("key_irq:%d\n",irq);
49    return IRQ_HANDLED ;
50}
51
52static int __init tiny4412_Key_irq_test_init(void) 
53{
54    int err = 0 ;
55    int irq_num1 ;
56    int data_t = 100 ;
57    //创建新队列和新工作者线程
58    mywork = create_workqueue("my work");
59    //初始化
60    INIT_WORK(&work,mywork_fuc);
61    //调度指定队列
62    queue_work(mywork,&work);
63    tasklet_init(&task_t,task_fuc,data_t);
64    printk("irq_key init\n");
65    irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
66    err = request_irq(irq_num1,irq_fuction,IRQF_TRIGGER_FALLING,"tiny4412_key1",(void *)"key1");
67    if(err != 0){
68        free_irq(irq_num1,(void *)"key1");
69        return -1 ;
70    }
71    return 0 ;
72}
73
74static void __exit tiny4412_Key_irq_test_exit(void) 
75{
76    int irq_num1 ;
77    printk("irq_key exit\n");
78    irq_num1 = gpio_to_irq(EXYNOS4_GPX3(2));
79    //销毁一条工作队列
80    destroy_workqueue(mywork);
81    free_irq(irq_num1,(void *)"key1");
82}
83
84module_init(tiny4412_Key_irq_test_init);
85module_exit(tiny4412_Key_irq_test_exit);
86
87MODULE_LICENSE("GPL");
88MODULE_AUTHOR("YYX");
89MODULE_DESCRIPTION("Exynos4 KEY Driver");

将程序编译完,将zImage下到板子上,重新启动会看到内核打印信息

640.jpg

   可以看到,当我们按下按键的时候,进入外部中断服务函数,此时task_fuc先被调用,然后调用到mywork_fuc,并打印了mywork_fuc里面的信息,从这里我们用程序验证了,工作队列是位于进程上下文,而不是中断上下文,和tasklet是有所区别的,下一节我们将会讲一讲tasklet(小任务机制)。

目录
相关文章
|
2月前
|
监控 Linux 开发者
理解Linux操作系统内核中物理设备驱动(phy driver)的功能。
综合来看,物理设备驱动在Linux系统中的作用是至关重要的,它通过与硬件设备的紧密配合,为上层应用提供稳定可靠的通信基础设施。开发一款优秀的物理设备驱动需要开发者具备深厚的硬件知识、熟练的编程技能以及对Linux内核架构的深入理解,以确保驱动程序能在不同的硬件平台和网络条件下都能提供最优的性能。
121 0
|
6月前
|
Linux
linux命令详细说明以及案例
本文介绍了常用的 Linux 命令及其详细说明和示例,包括:`ls`(列出目录内容)、`cd`(更改目录)、`rm` 和 `mv`(删除与移动文件)、`grep`(搜索文本)、`cat`(显示文件内容)以及 `chmod`(更改文件权限)。每个命令均配有功能描述、选项说明及实际案例,帮助用户更好地掌握 Linux 命令的使用方法。
248 56
|
4月前
|
数据挖掘 Linux 数据库
服务器数据恢复—Linux系统服务器数据恢复案例
服务器数据恢复环境: linux操作系统服务器中有一组由4块SAS接口硬盘组建的raid5阵列。 服务器故障: 服务器工作过程中突然崩溃。管理员将服务器操作系统进行了重装。 用户方需要恢复服务器中的数据库、办公文档、代码文件等。
|
3月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
101 0
|
4月前
|
安全 Ubuntu Linux
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
141 0
Nipper 3.8.0 for Windows & Linux - 网络设备漏洞评估
|
5月前
|
运维 安全 Linux
试试Linux设备命令行运维工具——Wowkey
WowKey 是一款专为 Linux 设备设计的命令行运维工具,提供自动化、批量化、标准化、简单化的运维解决方案。它简单易用、高效集成且无依赖,仅需 WIS 指令剧本文件、APT 账号密码文件和 wowkey 命令即可操作。通过分离鉴权内容与执行内容,WowKey 让运维人员专注于决策,摆脱繁琐的交互与执行细节工作,大幅提升运维效率与质量。无论是健康检查、数据采集还是配置更新,WowKey 都能助您轻松应对大规模设备运维挑战。立即从官方资源了解更多信息:https://atsight.top/training。
|
5月前
|
数据采集 运维 安全
Linux设备命令行运维工具WowKey问答
WowKey 是一款用于 Linux 设备运维的工具,可通过命令行手动或自动执行指令剧本,实现批量、标准化操作,如健康检查、数据采集、配置更新等。它简单易用,只需编写 WIS 指令剧本和 APT 帐号密码表文件,学习成本极低。支持不同流派的 Linux 系统,如 RHEL、Debian、SUSE 等,只要使用通用 Shell 命令即可通吃Linux设备。
|
6月前
|
Linux
linux常用命令详细说明以及案例
本文介绍了Linux中几个常用的命令及其用法,包括:`ls`(列出目录内容)、`cd`(切换目录)、`mkdir`(创建目录)、`rm -p`(删除目录及内容)和`mv`(移动或重命名文件/目录)。每个命令都配有详细说明、语法格式、常见选项及实用案例,帮助用户更好地理解和使用这些基础命令。内容源自[linux常用命令详细说明以及案例](https://linux.ciilii.com/show/news-285.html)。
153 7
|
6月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
9月前
|
人工智能 安全 Linux