Android2.3及Linux2.6.29内核模拟器版本编译与调试
一、Linux内核中断机制
1.同步中断:当一条指令执行完毕后,由CPU控制单元产生、而不是发生在代码指令执行期间的中断。也叫异常,例如系统调用。(注意:它运行在进程上下文!!!)
2.异步中断:由其他硬件设备依照CPU时钟信号随机产生、能够发生在指令执行过程中的中断。也叫硬件中断,例如键盘中断,这是狭义上的中断概念。(注意:异步中断内核都运行在中断上下文中。)
3.软中断:Linux2.6内核中软中断还是由函数do_softirq调用,调用时机如下:
硬件处理完一个中断之后(irq_exit函数):一定在中断上下文中。
内核线程ksoftirqd:一般在进程上下文中。
显式调用do_softirq的地方(比如net子系统中):一般在进程上下文中。
因此,软中断属于同步中断,但其可能运行在中断上下文、也可能运行在进程上下文。
广义上的中断应该包含上述两个部分。
二、中断上下文
在Linux系统中,异步中断发生,内核在进入中断之前都会保存当前的进程上下文、然后CPU的寄存器值变为当前发出中断信息的硬件设备的值,内核运行与中断上下文中;由于此时已经没有进程上下文,因此,需要避免使用会引起调度的函数(如msleep、信号量获取等)、因为这些函数都与具体进程相关。
三、中断底半部的实现方式
中断处理的矛盾:它需要完成大量的设备数据处理,同时、又不得不快速的退出。
解决方法:中断处理分成2部分,顶半部和底半部;顶半部是异步的,底半部是同步的。
下半部和上半部最大的不同:下半部是可中断的,而上半部是不可中断的,下半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断!
实现中断处理底半部的方式如下:
1.softirq和tasklet:这两种方式中断底半部可能运行与中断上下文,因此、不允许有会引起调度的函数使用(如msleep、信号量获取等);这时的中断底半部可以被其他irq打断。
2.workqueue:这种方式中断底半部运行与events/0内核线程的进程上下文,因此、允许有引起调度的函数使用(如msleep、信号量获取等);这时的中断底半部并没有在中断上下文中运行。
四、中断底半部的实现例子
1.tasklet方式
interrupt.c
#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> //add by tankai #include <linux/delay.h> //end tankai static int irq; static char *devname; static struct tasklet_struct mytasklet; static struct work_struct mywork; module_param(irq, int, 0644); module_param(devname, charp, 0644); struct myirq { int devid; }; struct myirq mydev={1119}; static void mytasklet_handler(unsigned long data) { printk("current1111111->pid is %d\n",current->pid); printk("current->pid is %d\n",current->pid); printk("in_interrupt() is %d\n",in_interrupt()); printk("in_irq() is %d\n",in_irq()); printk("in_softirq() is %d\n",in_softirq()); printk("tasklet is working..\n"); //情况一:使用不带进程调度的睡眠函数 mdelay(10000); /* //情况二:可以使用带进程调度的睡眠函数 msleep(10000); */ } static irqreturn_t myirq_handler(int irq, void *dev) { struct myirq mydev; static int count =0; mydev=*(struct myirq*)dev; printk("##################\n"); //msleep(10000); //中断顶半部不能调用可能会导致睡眠的函数 printk("key:%d..\n",count+1); //printk("devid:%d ISR is working..\n",mydev.devid); //printk("Bottom half will be working..\n"); printk("current->pid is %d\n",current->pid); printk("in_interrupt() is %d\n",in_interrupt()); printk("in_irq() is %d\n",in_irq()); printk("in_softirq() is %d\n",in_softirq()); //底半部实现一:tasklet(由内核线程——软中断(ksoftirqd/0)调度执行),不能睡眠 tasklet_init(&mytasklet, mytasklet_handler,0); //中断底半部,tasklet实现方式中、底半部也不能睡眠 tasklet_schedule(&mytasklet); //调度底半部 //此时,底半部在合适时机运行与软中断上下文 /* //底半部实现二:workqueue(由内核线程——events/0调度执行),可以睡眠 INIT_WORK(&mywork, mytasklet_handler); schedule_work(&mywork); //此时,底半部在合适时机运行与events/0的进程上下文 */ printk("ISR is leaving\n"); printk("##################\n"); count++; return IRQ_HANDLED; } static int __init myirq_init(void) { printk("Module is working..\n"); if(request_irq(irq, myirq_handler, IRQF_SHARED, devname, &mydev)!=0) { printk("%s request IRQ:%d failed..\n",devname,irq); return -1; } printk("%s rquest IRQ:%d success..\n",devname,irq); return 0; } static void __exit myirq_exit(void) { printk("Module is leaving"); /* //移除底半部一: tasklet_kill(&mytasklet); */ /* //移除底半部二: destroy_workqueue(&mywork); */ free_irq(irq, &mydev); printk("%s request IRQ :%d success...\n",devname ,irq); } module_init(myirq_init); module_exit(myirq_exit); MODULE_LICENSE("GPL");
Makefile
obj-m:=interrupt.o PWD:=$(shell pwd) CUR_PATH:=$(shell uname -r) KERNELPATH:= /home/android2.3/android2.3_kernel/ all: make -C $(KERNELPATH) M=$(PWD) modules clean: make -C $(KERNELPATH) M=$(PWD) clean
查看系统当前irq中断:
cat /proc/interruptsCPU0 1: 1 goldfish goldfish_pdev_bus 3: 8965 goldfish Goldfish Timer Tick 4: 0 goldfish goldfish_tty 10: 0 goldfish goldfish_rtc 11: 149 goldfish goldfish_tty 12: 364 goldfish eth0 13: 326 goldfish goldfish_fb 14: 1 goldfish goldfish_audio 15: 1241 goldfish goldfish_mmc 16: 0 goldfish goldfish-battery 17: 116 goldfish goldfish-events-keypad 18: 1 goldfish goldfish_switch 19: 0 goldfish goldfish_switch Err: 0
运行,与tty共享中断向量:
insmod interrupt.ko irq="11"
结果:current1111111->pid is 0 current->pid is 0 in_interrupt() is 256 //在中断上下文 in_irq() is 0 in_softirq() is 256 //而且是软中断、同步中断 tasklet is working..
2.workqueue方式
interrupt.c#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> //add by tankai #include <linux/delay.h> //end tankai static int irq; static char *devname; static struct tasklet_struct mytasklet; static struct work_struct mywork; module_param(irq, int, 0644); module_param(devname, charp, 0644); struct myirq { int devid; }; struct myirq mydev={1119}; static void mytasklet_handler(unsigned long data) { printk("current1111111->pid is %d\n",current->pid); printk("current->pid is %d\n",current->pid); printk("in_interrupt() is %d\n",in_interrupt()); printk("in_irq() is %d\n",in_irq()); printk("in_softirq() is %d\n",in_softirq()); printk("tasklet is working..\n"); /* //情况一:使用不带进程调度的睡眠函数 mdelay(10000); */ //情况二:可以使用带进程调度的睡眠函数 msleep(10000); } static irqreturn_t myirq_handler(int irq, void *dev) { struct myirq mydev; static int count =0; mydev=*(struct myirq*)dev; printk("##################\n"); //msleep(10000); //中断顶半部不能调用可能会导致睡眠的函数 printk("key:%d..\n",count+1); //printk("devid:%d ISR is working..\n",mydev.devid); //printk("Bottom half will be working..\n"); printk("current->pid is %d\n",current->pid); printk("in_interrupt() is %d\n",in_interrupt()); printk("in_irq() is %d\n",in_irq()); printk("in_softirq() is %d\n",in_softirq()); /* //底半部实现一:tasklet(由内核线程——软中断(ksoftirqd/0)调度执行),不能睡眠 tasklet_init(&mytasklet, mytasklet_handler,0); //中断底半部,tasklet实现方式中、底半部也不能睡眠 tasklet_schedule(&mytasklet); //调度底半部 //此时,底半部在合适时机运行与软中断上下文 */ //底半部实现二:workqueue(由内核线程——events/0调度执行),可以睡眠 INIT_WORK(&mywork, mytasklet_handler); schedule_work(&mywork); //此时,底半部在合适时机运行与events/0的进程上下文 printk("ISR is leaving\n"); printk("##################\n"); count++; return IRQ_HANDLED; } static int __init myirq_init(void) { printk("Module is working..\n"); if(request_irq(irq, myirq_handler, IRQF_SHARED, devname, &mydev)!=0) { printk("%s request IRQ:%d failed..\n",devname,irq); return -1; } printk("%s rquest IRQ:%d success..\n",devname,irq); return 0; } static void __exit myirq_exit(void) { printk("Module is leaving"); /* //移除底半部一: tasklet_kill(&mytasklet); */ /* //移除底半部二: destroy_workqueue(&mywork); */ free_irq(irq, &mydev); printk("%s request IRQ :%d success...\n",devname ,irq); } module_init(myirq_init); module_exit(myirq_exit); MODULE_LICENSE("GPL");
Makefile等同上,
运行,与tty共享中断向量:
insmod interrupt.ko irq="11"
结果:
current1111111->pid is 4 current->pid is 4 in_interrupt() is 0 //不在中断上下文 in_irq() is 0 in_softirq() is 0 tasklet is working..