一、工作队列
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
加载该模块