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月前
|
NoSQL Linux Redis
linux安装单机版redis详细步骤,及python连接redis案例
这篇文章提供了在Linux系统中安装单机版Redis的详细步骤,并展示了如何配置Redis为systemctl启动,以及使用Python连接Redis进行数据操作的案例。
71 2
|
2月前
|
人工智能 监控 Shell
常用的 55 个 Linux Shell 脚本(包括基础案例、文件操作、实用工具、图形化、sed、gawk)
这篇文章提供了55个常用的Linux Shell脚本实例,涵盖基础案例、文件操作、实用工具、图形化界面及sed、gawk的使用。
494 2
|
2月前
|
Unix Linux 网络安全
python中连接linux好用的模块paramiko(附带案例)
该文章详细介绍了如何使用Python的Paramiko模块来连接Linux服务器,包括安装配置及通过密码或密钥进行身份验证的示例。
91 1
|
2月前
|
监控 Linux Shell
30 个实用的 Linux 命令贴与技巧,提升你的效率(附实战案例)
本文介绍了30个实用的Linux命令及其应用场景,帮助你提升命令行操作效率。涵盖返回目录、重新执行命令、查看磁盘使用情况、查找文件、进程管理、网络状态监控、定时任务设置等功能,适合各水平的Linux用户学习和参考。
|
1月前
|
存储 Linux
服务器数据恢复—Linux操作系统网站服务器数据恢复案例
服务器数据恢复环境: 一台linux操作系统网站服务器,该服务器上部署了几十个网站,使用一块SATA硬盘。 服务器故障&原因: 服务器在工作过程中突然宕机。管理员尝试重新启动服务器失败,于是将服务器上的硬盘拆下检测,发现很多坏扇区。联系当地的一家数据恢复公司处理,但是没有成功。
|
2月前
|
存储 数据挖掘 Linux
服务器数据恢复—Linux操作系统网站服务器数据恢复案例
服务器数据恢复环境: 一台linux操作系统服务器上跑了几十个网站,服务器上只有一块SATA硬盘。 服务器故障: 服务器突然宕机,尝试再次启动失败。将硬盘拆下检测,发现存在坏扇区
|
2月前
|
Shell Linux Python
python执行linux系统命令的几种方法(python3经典编程案例)
文章介绍了多种使用Python执行Linux系统命令的方法,包括使用os模块的不同函数以及subprocess模块来调用shell命令并处理其输出。
38 0
|
3月前
|
存储 缓存 Unix
Linux 设备驱动程序(三)(上)
Linux 设备驱动程序(三)
39 3
|
3月前
|
Linux
Linux 设备驱动程序(四)
Linux 设备驱动程序(四)
24 1
|
3月前
|
存储 数据采集 缓存
Linux 设备驱动程序(三)(中)
Linux 设备驱动程序(三)
38 1

热门文章

最新文章

下一篇
无影云桌面