LINUX内核 tasklet && work
在内核中的中断机制中,为了防止解决中断嵌套(防止一个中断打断另一个中断)的问题,引进小任务机制:
实际上,takslet经常运用在于:在中断处理中大量使用tasklet机制;tasklet用于减少硬中断处理的时间,将本来是在硬中断服务程序中完成的任务转化成软中断完成,即是将一些非紧急的任务留到tasklet中完成,而紧急的任务则在硬中断服务程序中完成。
使用小任务机制需要三步:
第一:定义一个struct tasklet_struct的类;
第二步:初始化taskelet将处理任务的函数和takslet任务捆绑;
第三步:调度tasklet :tasklet_schedule(&tasklet);
12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <asm/uaccess.h> 18 19 #include <linux/gpio.h> 20 #include <mach/gpio.h> 21 #include <plat/gpio-cfg.h> 22 23 MODULE_LICENSE("GPL"); 24 MODULE_AUTHOR("bunfly"); 25 26 void my_tasklet_func(unsigned long data); 27 struct tasklet_struct my_tasklet; 28 29 int test_init() 30 { 31 printk("hello kernel\n"); 32 tasklet_init(&my_tasklet,my_tasklet_func, 0); 33 tasklet_schedule(&my_tasklet); 34 return 0; 35 } 36 37 void test_exit() 38 { 39 tasklet_kill(&my_tasklet); 40 printk("exit\n"); 41 } 42 43 module_init(test_init); 44 module_exit(test_exit); 45 46 void my_tasklet_func(unsigned long data) 47 { 48 printk("wang wang wang\n"); 49 } 50
以下是在定时狗中断中使用tasklet的实例:
1 #include <linux/init.h> 2 #include <linux/thread_info.h> 3 #include <linux/module.h> 4 #include <linux/sched.h> 5 #include <linux/errno.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/slab.h> 9 #include <linux/input.h> 10 #include <linux/init.h> 11 #include <linux/serio.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <asm/uaccess.h> 18 19 #include <linux/gpio.h> 20 #include <mach/gpio.h> 21 #include <plat/gpio-cfg.h> 22 23 MODULE_LICENSE("GPL"); 24 MODULE_AUTHOR("bunfly"); 25 26 irqreturn_t do_irq(int irq, void *data); 27 unsigned long wdt_virt; 28 unsigned long *wtcon, *wtdat, *wtcnt, *wtclrint; 29 struct clk *k; 30 31 void my_tasklet_func(unsigned long data) ; 32 struct tasklet_struct my_tasklet; 33 //定义一个小任务 34 35 int test_init() 36 { 37 int ret = 0; 38 ret = request_irq(IRQ_WDT, do_irq, IRQF_SHARED, "bunflydog", 0x1111); 39 if(ret < 0){ 40 printk("request_irq\n"); 41 return 1; 42 } 43 44 k = clk_get(NULL, "watchdog"); 45 clk_enable(k); 46 47 wdt_virt = ioremap(0x10060000, SZ_4K); 48 wtcon = wdt_virt + 0x00; 49 wtdat = wdt_virt + 0x04; 50 wtcnt = wdt_virt + 0x08; 51 wtclrint = wdt_virt + 0x0c; 52 53 *wtcon = 0 | (1 << 2) | (3 << 3) | (1 << 5) | (50 << 8); 54 *wtdat = 0x8000; 55 *wtcnt = 0x8000; 56 57 tasklet_init(&my_tasklet, my_tasklet_func, 555); 58 //小任务初始化 59 return 0; 60 } 61 62 void test_exit() 63 { 64 printk("exit\n"); 65 } 66 67 module_init(test_init); 68 module_exit(test_exit); 69 70 void my_tasklet_func(unsigned long data) 71 { 72 if (in_interrupt()) { 73 printk("KERNEL: my_tasklet_func in interrupt context.\n"); 74 } 75 76 printk("wang wang wang\n"); 77 78 //msleep(1); 79 } 80 81 irqreturn_t do_irq(int irq, void *data) 82 { 83 if (in_interrupt()) { 84 printk("KERNEL: do_irq in interrupt context.\n"); 85 } 86 87 tasklet_schedule(&my_tasklet); 88 89 *wtclrint = 250; 90 //清本次中断 91 return IRQ_HANDLED; 92 }
运行结果:
可以发现,my_taskelet_fun函数在do_irq函数调度之后才调用。
需要注意的是:tasklet机制中不允许睡眠,如果将代码中my_tasklet_fun函数中的睡眠加上就会反发生错误;
二.工作队列work
但是,有时候中断处理需要花费很长时间来响应中断,这时候就需要用到工作队列了。
类似于tasklet,work也需要三步:
1,第一步声明一个work;
work_struct my_work;
2, 初始化work;
INIT_WORK(my_work,my_mork_fun);
3,将work加入调度;
schedule_work(my_work);
以下是代码:
1 #include <linux/init.h> 2 #include <linux/thread_info.h> 3 #include <linux/module.h> 4 #include <linux/sched.h> 5 #include <linux/errno.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/slab.h> 9 #include <linux/input.h> 10 #include <linux/init.h> 11 #include <linux/serio.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <asm/uaccess.h> 18 19 #include <linux/gpio.h> 20 #include <mach/gpio.h> 21 #include <plat/gpio-cfg.h> 22 23 MODULE_LICENSE("GPL"); 24 MODULE_AUTHOR("bunfly"); 25 26 irqreturn_t do_irq(int irq, void *data); 27 unsigned long wdt_virt; 28 unsigned long *wtcon, *wtdat, *wtcnt, *wtclrint; 29 struct clk *k; 30 31 void my_work_func(unsigned long data) ; 32 struct work_struct my_work; 33 //1.定义工作队列 34 35 int test_init() 36 { 37 int ret = 0; 38 ret = request_irq(IRQ_WDT, do_irq, IRQF_SHARED, "bunflydog", 0x1111); 39 if(ret < 0){ 40 printk("request_irq\n"); 41 return 1; 42 } 43 44 k = clk_get(NULL, "watchdog"); 45 clk_enable(k); 46 47 wdt_virt = ioremap(0x10060000, SZ_4K); 48 wtcon = wdt_virt + 0x00; 49 wtdat = wdt_virt + 0x04; 50 wtcnt = wdt_virt + 0x08; 51 wtclrint = wdt_virt + 0x0c; 52 53 *wtcon = 0 | (1 << 2) | (3 << 3) | (1 << 5) | (50 << 8); 54 *wtdat = 0x8000; 55 *wtcnt = 0x8000; 56 57 INIT_WORK(&my_work, my_work_func); 58 //2初始化工作队列 59 return 0; 60 } 61 62 void test_exit() 63 { 64 printk("exit\n"); 65 } 66 67 module_init(test_init); 68 module_exit(test_exit); 69 70 void my_work_func(unsigned long data) 71 { 72 if (in_interrupt()) { 73 printk("KERNEL: my_work_func in interrupt context.\n"); 74 } 75 76 printk("wang wang wang\n"); 77 78 msleep(1); 79 } 80 81 irqreturn_t do_irq(int irq, void *data) 82 { 83 if (in_interrupt()) { 84 printk("KERNEL: do_irq in interrupt context.\n"); 85 } 86 schedule_work(&my_work); 87 //3.将工作队列假如调度策略 88 *wtclrint = 250; 89 return IRQ_HANDLED; 90 } 91