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/interrupts CPU0
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..