一、中断嵌套
当系统正在执行某中断处理函数时,又产生了一个新的中断,这就叫做中断嵌套。当中断为慢速中断时,新的中断会取代当前中断,即当前中断没有执行完就结束
了;当中断为快速中断时,新的终端就不会产生。这两种情况都是我们不愿意看到的情况,所以就有了今天的题目——中断分层。
二、中断分层
中断分层是将中断处理函数分为上下两个部分。上半部是与硬件相关的,下半部是与硬件无关的。与硬件无关的内容我们就可以将它分离出中断处理函数,交给内核在系统空闲的时候处理,这样就缩短了中断处理的时间,从而减小了中断丢失的几率。
分层方式:
1、软中断
2、tasklet
3、工作队列
三、工作队列处理方式
将中断下半部分的工作挂载到工作队列中去,在内核空闲的时候将工作调用。Linux内核使用struct workqueue_struct来描述一个工作队列,用struct work_struct来描述一个工作项。
struct workqueue_struct { unsigned int flags; /* I: WQ_* flags */ union { struct cpu_workqueue_struct __percpu *pcpu; struct cpu_workqueue_struct *single; unsigned long v; } cpu_wq; /* I: cwq‘s */ struct list_head list; /* W: list of all workqueues */ struct mutex flush_mutex; /* protects wq flushing */ int work_color; /* F: current work color */ int flush_color; /* F: current flush color */ atomic_t nr_cwqs_to_flush; /* flush in progress */ struct wq_flusher *first_flusher; /* F: first flusher */ struct list_head flusher_queue; /* F: flush waiters */ struct list_head flusher_overflow; /* F: flush overflow list */ mayday_mask_t mayday_mask; /* cpus requesting rescue */ struct worker *rescuer; /* I: rescue worker */ int saved_max_active; /* W: saved cwq max_active */ const char *name; /* I: workqueue name */ };
struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; };
1、创建工作队列
create_workqueue(name)
参数:name为const char *类型,创建工作队列的名字
返回值:struct workqueue_struct*型指针
2、创建工作
INIT_WORK(_work, _func)
参数:
work:创建好工作项变量的地址
func:工作项中的成员func的名字
无返回值
3、提交工作
即将工作项挂载到工作队列中去。
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
参数:
wq:工作队列变量的地址
work:工作项变量的地址
其实Linux内核已创建好了一个默认的工作队列供用户使用,keventd_wq,这样我们就不用再去进行创建工作队列的工作。提交工作项到默认工作队列的函数为
int schedule_work(struct work_struct *work)
参数:要提交工作项的地址
工作队列应用代码:
#include <linux/module.h> #include <linux/init.h> struct work_struct *work1,*work2; void work1_func() { printk("this is work1\n"); } void work2_func() { printk("this is work2\n"); } int que_init() { /*创建工作*/ work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL); INIT_WORK(work1, work1_func); /*挂载工作到工作队列*/ schedule_work(work1); /*创建工作*/ work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL); INIT_WORK(work2, work2_func); /*挂载工作到工作队列*/ schedule_work(work2); return 0; } void que_exit() { } MODULE_LICENSE("GPL"); module_init(que_init); module_exit(que_exit);
将中断分层应用到键盘中断驱动程序中:
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/io.h> #define GPGCON 0x56000060 /*定义工作队列*/ struct work_struct *work1; void work1_func() { printk("press key down\n"); } int que_init() { /*创建工作*/ work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL); INIT_WORK(work1, work1_func); } /*中断处理函数*/ irqreturn_t key_int(int irq, void *dev_id) { /*1、检测设备是否产生中断*/ /*2、清除中断产生标志*/ /*3、提交下半部分工作*/ schedule_work(work1); return 0; } void key_hw_init() { unsigned int data; unsigned int *gpio_config; gpio_config = ioremap(GPGCON,4); data = readw(gpio_config); data &= ((3)|(3<<6)|(3<<10)|(3<<12)|(3<<14)|(3<<22));//~(0b11); data |= (2|(2<<6)|(2<<10)|(2<<12)|(2<<14)|(2<<22));//0b10; writew(data,gpio_config); } int key_open(struct inode *node, struct file *filp) { return 0; } struct file_operations key_fops = { .open = key_open, //.unlocked_ioctl = key_ioctl, }; struct miscdevice key_miscdev = { .minor = 200, .name = "key", .fops = &key_fops, }; static int key_init() { /*注册设备*/ misc_register(&key_miscdev); /*硬件初始化*/ key_hw_init(); /*注册中断*/ request_irq(IRQ_EINT8,key_int,IRQF_TRIGGER_FALLING ,"key", 0); request_irq(IRQ_EINT11,key_int,IRQF_TRIGGER_FALLING ,"key", 0); request_irq(IRQ_EINT13,key_int,IRQF_TRIGGER_FALLING ,"key", 0); request_irq(IRQ_EINT14,key_int,IRQF_TRIGGER_FALLING ,"key", 0); request_irq(IRQ_EINT15,key_int,IRQF_TRIGGER_FALLING ,"key", 0); request_irq(IRQ_EINT19,key_int,IRQF_TRIGGER_FALLING ,"key", 0); que_init(); printk("key.ko is ready\n"); return 0; } static void key_exit() { /*注销设备*/ misc_deregister(&key_miscdev); /*注销中断*/ free_irq(IRQ_EINT8, 0); } MODULE_LICENSE("GPL"); module_init(key_init); module_exit(key_exit);
实现功能:
当按下一个按键,在串口中打印出对应的信息。
此代码适用mini2440开发板,不同型号开发板IO口和中断号不同。如果有疑问或建议,欢迎指出。