Linux处理嵌套中断
1.慢速中断情况下
第一种情况:当中断处理1正在执行的时候,中断处理2(不同类型中断)到来,那么会暂停中断处理1,转向执行中断处理2,待中断处理2执行完后,再接着执行中断处理1。
第二种情况:当中断处理1正在执行的时候,中断处理2(同类型中断)来到,那么会忽略中断处理2,接着执行中断处理1。
2.快速中断情况下
第一种情况:当中断处理1正在执行的时候,中断处理2(不同类型中断)来到,那么会忽略中断处理2,接着执行中断处理1。
第二种情况:当中断处理1正在执行的时候,中断处理2(同类型中断)来到,那么会忽略中断处理2,接着执行中断处理1。
在慢速中断的同类型中断和快速中断情况下会引起中断丢失。如何解决中断丢失,就要用到中断分层的技术。
中断分层技术
如何尽量缩短中断处理时间?分析中断处理程序主要处理的是与硬件相关的工作和硬件无关的工作。把中断处理程序分层为上半部和下半部两个部分来做,把与硬件相关的工作放在上半部(中断处理)来做,与硬件无关的工作放在下半部(内核线程)来做,就是中断分层技术。
中断分层方式
1.软中断
2.tasklet
3.工作队列
工作队列
工作队列是一种将任务推后执行的形式,它把推后的任务交给一个内核线程去执行。这样下半部会在进程上下文执行,它允许重新调度甚至睡眠。每个被推后的任务叫做“工作”,由这些工作组成的队列称为工作队列。
首先中断处理程序按照工作队列的格式把下半部线程的一个工作挂载到处理链表上去;然后内核线程在适当的时候会扫描链表,把工作拿来执行。这样就尽量减少了中断丢失的可能。
工作队列结构
Linux内核使用struct workqueue_struct来描述一个工作队列:
struct workqueue_struct{
struct cpu_workqueue_struct *cpu_wq;
struct list_head list;
const char *name; //workqueue name
int singlethread;
int freezeable; //freeze threads during suspend
int rt;
};
Linux内核使用struct work_struct来描述一个工作项:
struct work_struct{
atomic_long_t data;
struct list_head entry;
work_func_t func; //用来指明要执行的工作
};
typedef void (*work_func_t)(struct work_struct *work);
工作队列使用
1.创建工作队列
create_workqueue
2.创建工作
INIT_WORK
3.提交工作
queue_work
工作队列示例
使用工作队列模块必须遵循GPL协议
workqueue.c
/******************************************************************** *头文件 *********************************************************************/ #include <linux/init.h> #include <linux/module.h> #include <linux/workqueue.h> /******************************************************************** *模块安装 *********************************************************************/ //处理工作 void workFunc0(struct work_struct *work){ printk("this is a work0 "); } void workFunc1(struct work_struct *work){ printk("this is a work1 "); } //安装模块 struct work_struct *work0; struct work_struct *work1; static int queue_init(void){ //创建队列 struct workqueue_struct *queue; queue = create_workqueue("queue"); //创建工作 work0 = kmalloc(sizeof(struct work_struct), GFP_KERNEL); work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL); INIT_WORK(work0, workFunc0); INIT_WORK(work1, workFunc1); //提交工作 queue_work(queue, work0); queue_work(queue, work1); return 0; } //卸载模块 static void queue_exit(void){ } /******************************************************************** *模块声明 *********************************************************************/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("D"); MODULE_DESCRIPTION(""); MODULE_VERSION("v1.0"); module_init(queue_init); module_exit(queue_exit);
系统工作队列
在大多数情况下,驱动程序并不需要自己创建工作队列,只需要定义工作,然后提交到内核已经定义好的工作队列keventd_wq中。
1.提交工作队列
schedule_work
使用默认工作队列
workqueue.c
/******************************************************************** *头文件 *********************************************************************/ #include <linux/init.h> #include <linux/module.h> /******************************************************************** *模块安装 *********************************************************************/ //处理工作 void workFunc0(struct work_struct *work){ printk("this is a work0 "); } void workFunc1(struct work_struct *work){ printk("this is a work1 "); } //安装模块 struct work_struct *work0; struct work_struct *work1; static int queue_init(void){ //创建工作 work0 = kmalloc(sizeof(struct work_struct), GFP_KERNEL); work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL); INIT_WORK(work0, workFunc0); INIT_WORK(work1, workFunc1); //提交工作 schedule_work(work0); schedule_work(work1); return 0; } //卸载模块 static void queue_exit(void){ } /******************************************************************** *模块声明 *********************************************************************/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("D"); MODULE_DESCRIPTION(""); MODULE_VERSION("v1.0"); module_init(queue_init); module_exit(queue_exit);
对按键驱动中断处理进行分层
/******************************************************************** *头文件 *********************************************************************/ #include <linux/init.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/fs.h> /******************************************************************** *宏定义 *********************************************************************/ #define GPGCON 0x56000060 //按键物理地址 /******************************************************************** *全局变量 *********************************************************************/ unsigned int *keyCon; //按键指针 struct work_struct *work; //按键工作 /******************************************************************** *中断处理 *********************************************************************/ //处理中断下部 void key_work(struct work_struct *work){ printk("key down! "); } //处理中断上部 irqreturn_t key_irq(int irq, void *dev_id){ //提交工作 schedule_work(work); return 0; } /******************************************************************** *设备方法 *********************************************************************/ //打开设备 int key_open(struct inode *node, struct file *filp){ return 0; } //关闭设备 int key_close(struct inode *node, struct file *filp){ return 0; } //设备方法 struct file_operations key_fops = { .open = key_open, .release = key_close }; /******************************************************************** *模块安装 *********************************************************************/ //混杂设备 struct miscdevice misdev = { .minor = 200, //次设备号 .name = "keydev", //设备名称 .fops = &key_fops //设备方法 }; //初始硬件 void handware_init(void){ unsigned short keyTmp; keyCon = ioremap(GPGCON, 4); //虚拟地址映射 keyTmp = readw(keyCon); //获取GPGCON值 keyTmp &= ~(0x3<<0); //GPG0[1:0]:00 keyTmp |= (0x2<<0); //GPG0[1:0]:EINT[8] writew(keyTmp, keyCon); //设置GPGCON值 } //安装模块 static int key_init(void){ //注册混杂设备 misc_register(&misdev); //注册中断处理 request_irq(IRQ_EINT8, key_irq, IRQF_TRIGGER_FALLING, "keyirq", 0); //下降沿触发,IRQ_EINT8定义在irqs.h文件中 //初始工作队列 work = kmalloc(sizeof(struct work_struct), GFP_KERNEL); INIT_WORK(work, key_work); //初始硬件设备 handware_init(); return 0; } //卸载模块 static void key_exit(void){ //注销混杂设备 misc_deregister(&misdev); //注销中断处理 free_irq(IRQ_EINT8, 0); } /******************************************************************** *模块声明 *********************************************************************/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("D"); MODULE_DESCRIPTION(""); MODULE_VERSION("v1.0"); module_init(key_init); module_exit(key_exit);