欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > Linux系统驱动(十五)中断底半部---tasklet、工作队列

Linux系统驱动(十五)中断底半部---tasklet、工作队列

2024/10/24 11:14:56 来源:https://blog.csdn.net/weixin_44254079/article/details/141122308  浏览:    关键词:Linux系统驱动(十五)中断底半部---tasklet、工作队列

文章目录

  • 一、概念
  • 二、tasklet底半部机制
    • (一)简介
    • (二)tasklet的API接口
    • (三)tasklet底半部处理函数
  • 三、工作队列
    • (一)简介
    • (二)API
    • (三)使用示例

一、概念

在中断顶半部处理函数中只能做简短的不耗时的操作,但有的时候有希望在中断到来的时候做尽可能多的事情,所以两者就产生的矛盾,内核为了解决这一矛盾设计了中断底半部机制。
在中断顶半部处理函数中只能做简短的不耗时的操作,处理的是紧急的、不耗时的任务;中断底半部处理的是不紧急的、耗时的任务

中断底半部机制一共有三种:
软中断,tasklet,工作队列

  • 注:软中断有个数限制(32个),一般不在驱动中使用

二、tasklet底半部机制

(一)简介

基于软中断实现的,没有个数限制,因为它内部是通过链表实现的。
tasklet是中断的一个部分,不能够脱离中断顶半部单独执行,在中断顶半部执行即将结束时,可以开启tasklet底半部机制。
tasklet是工作在中断上下文的,tasklet的底半部处理函数中只能做耗时任务或者短延时任务,但不能做休眠工作
(底半部优先级高于进程,他是工作在中断上下文的)

  • 注:ARM架构中,中断不允许套中断
  • tasklet绝不能做休眠动作,内核会崩溃(表现为乱码刷屏,不停止)

(二)tasklet的API接口

1. 分配对象
struct tasklet_struct{struct tasklet_struct *next; //构成链表成员unsigned long state;   //是否被触发的状态atomic_t count;        //触发的次数bool use_callback;     //处理函数的选择 false->func   true->callbackunion {void (*func)(unsigned long data); //旧版本的处理函数void (*callback)(struct tasklet_struct *t);//新版本的处理函数};unsigned long data; //向底半部处理函数传递的参数};struct tasklet_struct tasklet;2. 初始化对象void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data) //旧版本的初始化void tasklet_setup(struct tasklet_struct *t,void (*callback)(struct tasklet_struct *)) //新版本的初始化3. 调用执行
void tasklet_schedule(struct tasklet_struct *t)

(三)tasklet底半部处理函数

#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> //设备树文件相关头文件
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/of_gpio.h>
// mykeys{
// 	interrupt-parent = <&gpiof>;
// 	interrupt = <7 0>,<8 0>,<9 0>;
// };struct device_node *key_node;
unsigned int key_gpiono[3];struct timer_list mytimer;//定时器
//7 8 9------2 3 1
unsigned int irqno[3]={0};struct tasklet_struct mytasklet;  //1.定义结构体
void mytasklet_bottom_func(struct tasklet_struct *tasklet){int i=0;for(i=0;i<50;i++){printk("i = %d\n",i);}
}
void timer_handler(struct timer_list* timer){int i;for(i=0;i<3;i++){if(!gpio_get_value(key_gpiono[i])){switch(i){case 0:printk("key1 down ......");break;case 1:printk("key2 down ......");break;case 2:printk("key3 down ......");break;}}}tasklet_schedule(&mytasklet);
}irqreturn_t irq_handler(int irq, void *dev){mod_timer(&mytimer,jiffies+1);return IRQ_HANDLED;
}static int __init mynode_init(void){int i;/***GPIO***///1. 获取节点key_node = of_find_node_by_path("/mykeys");if(NULL == key_node){pr_err("of_find_node_by_path error");return -EINVAL;}printk("of_find_node_by_path success\n");//2.获取gpio号for(i=0;i<3;i++){//corekey_gpiono[i] = of_get_named_gpio(key_node,"keys",i);if(key_gpiono[i] < 0){pr_err("of_get_named_gpio error");return key_gpiono[i];}}printk("of_get_named_gpio success\n");//3. 申请gpio,是为了防止竞态/***中断***///1. 获取节点key_node = of_find_node_by_name(NULL,"mykeys");if(NULL == key_node){pr_err("of_find_node_by_name error");return -EINVAL;}//2.获取中断号for(i=0;i<3;i++){irqno[i] = irq_of_parse_and_map(key_node,i);if (irqno[i] == 0) {pr_err("irq_of_parse_and_map error\n");return -EAGAIN;}}//3.注册中断号for(i=0;i<3;i++){request_irq(irqno[i],irq_handler,IRQF_TRIGGER_FALLING,"my_IRQ_test",(void *)irqno[i]);}/***定时器****///2.定时器对象初始化mytimer.expires = jiffies+1; //定时10ms timer_setup(&mytimer, timer_handler, 0); //3.启动定时器add_timer(&mytimer);tasklet_setup(&mytasklet,mytasklet_bottom_func);return 0;
}
static void __exit mynode_exit(void){int i=0;//注销中断号for(i=0;i<3;i++){free_irq(irqno[i],(void *)irqno[i]);}
}module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");
  • 注:此时安装时就会触发一次,因为此处使用了定时器中断
  • 在这里插入图片描述

三、工作队列

(一)简介

linux内核在启动时会默认启动一个events线程,这个线程默认处于休眠状态,它的内部维护一个工作队列。将

没有个数限制(链式队列)
工作队列可以脱离中断顶半部单独执行。工作队列工作在进程上下文,所以在工作队列的底半部处理函数中可以做延时,耗时,甚至休眠操作

必须等工作队列底半部处理函数结束后才能卸载函数,否则会出现空指针,导致内核崩溃。可以使用cancel_

(二)API

1.分配对象struct work_struct {atomic_long_t data; //结构体内部的data变量struct list_head entry; //构成队列成员work_func_t func;  //工作队列的底半部处理函数//typedef void (*work_func_t)(struct work_struct *work);}struct work_struct work;
2.初始化对象INIT_WORK(&work, 底半部处理函数);
3.调用执行schedule_work(struct work_struct *work) 
4.延时取消工作队列cancel_work_sync(&work); //等待工作队列执行结束,在卸载驱动

(三)使用示例


版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com