linux工作队列应用实例

一、工作队列

linux实现中断下文的机制主要有4种:
1.软中断 2.tasklet 3.工作队列 4.线程化irq

本文主要介绍中断下文机制中的工作队列,工作队列与tasklet最大的区别就是tasklet不能休眠,工作队列可以休眠,所以tasklet多用来处理比较耗时间的任务,而工作队列可以处理非常复杂并且更耗时间的事情。

**实现原理:**工作队列的实现机制非常复杂,简单的来说就是,Linux系统在启动期间会创建内核线程,该线程创建以后就处于sleep状态,然后这个线程会一直去队列里面读,看看有没有任务,如果有就执行,没有就休眠。

**共享工作队列:**多个不同的程序共用,不需要自己创建,但如果前面的工作比较耗时的话,就会影响到后面的工作。

**自定义工作队列:**共享工作队列无法满足需要时,需要我们自己创建,其系统的开销大,优点是不会受到其他工作的影响。

二、内核提供的接口

内核源码路径:/include/linux/workqueue.h 定义了工作队列的结构体。

struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
};

2.1 宏DECLARE_WORK

#dedfine DECLARE_WORK(n,f);

功能:静态定义并且初始化工作队列。

2.2 宏INIT_WORK:

#define INIT_WORK (_work,_func);

功能:动态定义并且初始化工作队列。
参数介绍:
work:工作队列地址;
func:工作函数;

2.3 schedule_work函数;

int schedule_work(struct work_struct *work);

功能:调度工作,把work_struct挂到cpu相关的工作队列链表上,等待工作者线程处理。
参数介绍:
work:工作队列地址。
在此,调度完工作,并不会马上执行,只是加到了共享队列里面去,等轮到时才会执行。
如果多次调用相同的任务,假如上一次的任务还没有完成,那么多次调度相同的任务是无效的。

三、实例代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/workqueue.h>


struct device_node *test_device_node;
struct property *test_node_property;
struct work_struct key_test;

int gpio_num;
int irq;

//中断上文函数
irq_handler_t test_key(int irq, void *args)
{
    printk("Start!\n");
    //唤醒工作队列
    schedule_work(&key_test);
    printk("End!\n");
    return IRQ_HANDLED;//接收到了准确的中断信号,并且做了相应正确的处理
}

//中断下文函数
void work_queue_test(unsigned long data)
{
    int i = 0;
    while(i<50){
        i++;
        printk("test_key is %d.\n",i);
    }
}

//探针函数,用于设备的初始化    
int dts_probe(struct platform_device *pdev)
{
    int ret = 0;
    printk("dts_probe matching ok!\n");

    //test_device_node = of_find_node_by_name(NULL,"test_key");//根据节点名字索引设备树节点
    
    test_device_node = of_find_node_by_path("/test_key");//根据据对路径索引设备树节点
    if(test_device_node == NULL){
        printk("Failture to find device node %s","test_key");
        return -1;
    }

    //获取gpio的编号
    gpio_num = of_get_named_gpio(test_device_node,"key-gpio",0);
    if(gpio_num < 0){
        printk("Failture to get gpio %s","key-gpio");
        return -1;
    }

    //设置gpio的方向
    gpio_direction_input(gpio_num);

    //irq = irq_of_parse_and_map(test_device_node,0);
    irq = gpio_to_irq(gpio_num);//同上一句代码,为gpio申请中断号
    printk("irq is %d\n",irq);
    
    //申请并注册中断资源
    ret = request_irq(irq,test_key,IRQF_TRIGGER_RISINNG,"test_key",NULL);
    if(ret < 0){
        printk("Failed to requst irq\n")
        return -1;
    }

    //初始化key_test这个工作队列,并且将work_queue_test添加进工作队列
    INIT_WORK(&key_test,work_queue_test);
    return 0;
    
}

int dts_remove(struct platform_device *pdev)
{
    printk("dts_remove!\n");
    return 0;
}

//根据设备名字匹配
const struct platform_device_id dts_idtable = {
    .name = "dts_test1"
}

//根据compatible匹配
const struct of_device_id of_match_table_test[] = {
    {.compatible = "led_keys"},
    {}
}

//初始化dts_driver结构体
struct platform_driver dts_driver = {
    .probe = dts_probe,
    .remove = dts_remove,
    .driver = {
        .ower = THIS_MODULE,
        .name = "dts_test2",
        .of_match_table = of_match_table_test
    },
    .id_table = &dts_idtable
};

//驱动初始化函数
static int dts_driver_init(void)
{
    int ret = 0;
    ret = platform_driver_register(&dts_driver);
    if(ret < 0){
        printk("platform driver register error\n");
        return ret;
    }
    printk("platform driver register of!\n");
    return 0;
}

//驱动卸载函数
static int dts_driver_exit(void)
{
    printk("dts_driver_exit!\n");
    free_irq(irq,NULL);
    platform_driver_unregister(&dts_driver);
}

//模块加载
module_init(dts_driver_init);
//模块卸载
module_exit(dts_driver_exit);
/*模块许可证声明,模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则在加载此模块时,
 *会收到内核被污染“kernel tainted” 的警告 */
MODULE_LICENSE("GPL");

3.2 Makefile文件

obj-m += work_queue.o
KDIR:=/linux/linux-4.1.15
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean

当系统启动完成之后执行:

insmod work_queue

加载该模块

猜你喜欢

转载自blog.csdn.net/weixin_44698673/article/details/126996738
今日推荐