中断分为两个部分:中断顶部(top half)和中断底半部(bootom half)
一、中断顶部(top half)
中断上半部需要处理一下三种情况:
1,如果一个任务对时间非常敏感,将其放在中断处理程序中执行。
2,如果一个任务和硬件相关,将其放在中断处理程序中执行。
3,如果一个人物要保证不被其他中断(特别是相同的中断)打断,将其放在中断处理程序中 执行。
4,其他所有任务,考虑放在中断底半部去执行。
一下代码都是基于s5p6818开发平台按键实验
头文件<linux/interrupt.h>
//中断请求函数
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, \
const char *name,void *dev);
功能:向内核请求注册中断
参数:
@irq 中断号
gpio_to_irq() //将GPIO号转化成中断号
@handler 中断处理函数指针
//中断处理函数
typedef irqreturn_t (*irq_handler_t)(int , void *); 函数指针类型
irqreturn_t 返回值类型是枚举类型
IRQ_NONE 没有处理
IRQ_HANDLED 已经处理完成
irqreturn_t irq_handler(int irq,void*args)
{
}
@flags 中断标志
中断的触发方式(中断标志中的一种):
IRQF_TRIGGER_RISING 上升沿触发
IRQF_TRIGGER_FALLING 下降沿触发
IRQF_TRIGGER_HIGH 高电平触发
IRQF_TRIGGER_LOW 低电平触发
IRQF_DISABLED 屏蔽同级别中断
IRQF_SHARED 中断共享
@name 中断名字
@dev 私有数据
返回值:成功返回0,失败返回负数错误码
//中断注销函数
void free_irq(unsigned int, void *); //释放/注销中断
下方代码是在s5p6818开发平台上进行的按键中断
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#define GPIOA28 28
#define NAME "key_gpioa28"
int irqno = 0;
/*中断处理函数*/
irqreturn_t irq_handler(int irqno, void *args)
{
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
return IRQ_HANDLED; //处理中断后返回的已处理标志
}
int __init key_int_init(void)
{
int ret = 0;
irqno = gpio_to_irq(GPIOA28); //通过gpio号获取中断号
/*向内核请求注册中断*/
ret = request_irq(irqno,irq_handler,IRQF_TRIGGER_FALLING|IRQF_DISABLED, \
NAME, NULL);
if(ret < 0){
printk(KERN_ERR "request_irq failed...\n");
return ret;
}
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
return 0;
}
void __exit key_int_exit(void)
{
/*释放中断*/
free_irq(irqno,NULL);
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
}
module_init(key_int_init);
module_exit(key_int_exit);
MODULE_LICENSE("GPL");
二、中断底半部(top half)
【1】软中断
特点:可以被中断(顶半部)打断,不可以被中断底半部打断,不参与进程调度
要求:可以有耗时操作(相对来将) —>>> 可以使用for ,不可以使用延时和睡眠函数
void open_softirq(int nr, void (*action)(struct softirq_action *))
void raise_softirq(unsigned int nr)
【2】tasklet tasklet依赖软中断实现
特点:可以被中断(顶半部)打断,不可以被中断底半部打断,不参与进程调度
要求:可以有耗时操作(相对来将) —>>> 可以使用for ,不可以使用延时和睡眠函数
数据类型:
struct tasklet_struct
{
struct tasklet_struct *next; //内核使用链表方式管理的时候使用的
unsigned long state;
atomic_t count;
void (*func)(unsigned long); //中断底半部处理函数
unsigned long data; //用户数据
};
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data);
功能:初始化tasklet
参数:
@t tasklet结构体指针
@func 中断底半部处理函数
@data 私有数据
void tasklet_schedule(struct tasklet_struct *t)
功能:调度tasklet底半部
参数:
@t tasklet结构体指针
实验代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#define GPIOA28 28
#define NAME "key_gpioa28"
int irqno = 0;
struct tasklet_struct task;
/*由tasklet实现的中断底半部处理函数*/
void task_func(unsigned long args)
{
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
}
/*中断顶半部执行处理函数*/
irqreturn_t irq_handler(int irqno, void *args)
{
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
tasklet_schedule(&task);
return IRQ_HANDLED;
}
int __init key_int_init(void)
{
int ret = 0;
irqno = gpio_to_irq(GPIOA28);
ret = request_irq(irqno,irq_handler,IRQF_TRIGGER_FALLING|IRQF_DISABLED, \
NAME, NULL);
if(ret < 0){
printk(KERN_ERR "request_irq failed...\n");
return ret;
}
tasklet_init(&task,task_func,0);
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
return 0;
}
void __exit key_int_exit(void)
{
free_irq(irqno,NULL);
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
}
module_init(key_int_init);
module_exit(key_int_exit);
MODULE_LICENSE("GPL");
【3】工作队列
特点:可以被中断(顶半部)打断,也可以被中断底半部打断,也参与进程调度
要求:可以有耗时操作,也可以有涉及进程调度相关函数
//头文件
<linux/workqueue.h>
INIT_WORK(_work, _func)
功能:初始化工作
参数:
@_work struct work_struct 指针
struct work_struct
{
atomic_long_t data;
work_func_t func;
};
@_func 工作队列底半部处理函数
/*工作队列实现的底半部处理函数指针*/
typedef void (*work_func_t)(struct work_struct *work);
int schedule_work(struct work_struct *work);
功能:工作队列调度工作底半部处理函数
参数:
@work struct work_struct 指针
create_workqueue(name)
功能:创建工作队列
参数:
@name 名字
返回值:返回struct workqueue_struct结构体指针
int queue_work(struct workqueue_struct *wq, struct work_struct *work);
功能:将工作添加在工作队列里
参数:
@wq 工作队列结构体
@work 工作结构体
第一种方法
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#define GPIOA28 28
#define NAME "key_gpioa28"
int irqno = 0;
struct work_struct work;
struct workqueue_struct *work_queue;
/*由工作队列实现的中断底半部处理函数*/
void work_func(struct work_struct *work)
{
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
}
/*中断顶半部执行处理函数*/
irqreturn_t irq_handler(int irqno, void *args)
{
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
queue_work(work_queue, &work); //添加工作到工作队列中,等待被调度
return IRQ_HANDLED;
}
int __init key_int_init(void)
{
int ret = 0;
irqno = gpio_to_irq(GPIOA28);
ret = request_irq(irqno,irq_handler,IRQF_TRIGGER_FALLING|IRQF_DISABLED, \
NAME, NULL);
if(ret < 0){
printk(KERN_ERR "request_irq failed...\n");
return ret;
}
/*创建工作队列*/
work_queue = create_workqueue("key_work_queue");
/*初始化工作*/
INIT_WORK(&work,work_func);
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
return 0;
}
void __exit key_int_exit(void)
{
free_irq(irqno,NULL);
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
}
module_init(key_int_init);
module_exit(key_int_exit);
MODULE_LICENSE("GPL");
第二种方法
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#define GPIOA28 28
#define NAME "key_gpioa28"
int irqno = 0;
struct work_struct work;
struct workqueue_struct *work_queue;
/*由工作队列实现的中断底半部处理函数*/
void work_func(struct work_struct *work)
{
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
}
/*中断顶半部执行处理函数*/
irqreturn_t irq_handler(int irqno, void *args)
{
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
queue_work(work_queue, &work);
return IRQ_HANDLED;
}
int __init key_int_init(void)
{
int ret = 0;
irqno = gpio_to_irq(GPIOA28);
ret = request_irq(irqno,irq_handler,IRQF_TRIGGER_FALLING|IRQF_DISABLED, \
NAME, NULL);
if(ret < 0){
printk(KERN_ERR "request_irq failed...\n");
return ret;
}
work_queue = create_workqueue("key_work_queue");
INIT_WORK(&work,work_func);
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
return 0;
}
void __exit key_int_exit(void)
{
free_irq(irqno,NULL);
printk(KERN_INFO "---%s---%s--%d---\n",__FILE__,__func__,__LINE__);
}
module_init(key_int_init);
module_exit(key_int_exit);
MODULE_LICENSE("GPL");
内核提供的延时机制:
1.中断底半部
2.定时器
3.延时函数和睡眠函数(delay.h)