在内核调试中,会经常出现内核僵死的问题,也就是发生死循环,内核不能产生调度。导致内核失去响应。这种情况下我们可以采用修改系统内核中的系统时钟的中断来定位发生僵死的进程和函数名称。因为内核系统系统时钟采用的是硬件中断的形式存在,所以,软件发生僵死的时候,系统时钟照样会发生中断。
1.1、我们在命令行输入:# cat /proc/interrupts
# cat /proc/interrupts CPU0 30: 8316 s3c S3C2410 Timer Tick -----> 系统时钟 33: 0 s3c s3c-mci 34: 0 s3c I2SSDI 35: 0 s3c I2SSDO 37: 12 s3c s3c-mci 42: 0 s3c ohci_hcd:usb1 43: 0 s3c s3c2440-i2c 51: 1047 s3c-ext eth0 60: 0 s3c-ext s3c-mci 70: 16 s3c-uart0 s3c2440-uart 71: 26 s3c-uart0 s3c2440-uart 79: 8 s3c-adc s3c2410_action 80: 1732 s3c-adc s3c2410_action 83: 0 - s3c2410-wdt Err: 0 #
30: 8316 s3c S3C2410 Timer Tick 这个就是系统时钟,中断号为301.2、在内核代码中搜索"S3C2410 Timer Tick"字样。
在Time.c (arch\arm\plat-s3c24xx)文件中有如下代码。
static struct irqaction s3c2410_timer_irq = { .name = "S3C2410 Timer Tick", .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, .handler = s3c2410_timer_interrupt, }; /* * IRQ handler for the timer */ static irqreturn_t s3c2410_timer_interrupt(int irq, void *dev_id) { #if 1 static pid_t pre_pid; static int cnt=0; //时钟中断的中断号是30 if(irq==30) { if(pre_pid==current->pid) { cnt++; } else { cnt=0; pre_pid=current->pid; } //如果本进程十秒钟还没有离开的话,就会打印下面的语句 if(cnt==10*HZ) { cnt=0; printk("s3c2410_timer_interrupt : pid = %d, task_name = %s\n",current->pid,current->comm); } } #endif write_seqlock(&xtime_lock); timer_tick(); write_sequnlock(&xtime_lock); return IRQ_HANDLED; }
①、每个进程都有一个结构task_struct用来存储进程的一些状态信息。current是一个宏,表示当前进程的信息,也就是一个task_struct结构体,所以current->pid为当前进程的pid号,current->comm表示当前进程的name。
②、HZ也是一个宏定于,表示1s需要多少次中断。10*HZ表示就就是10s需要多少次中断!
2、测试编译内核:#make uImage
加载一个带有while(1);的驱动程序,系统发送僵死,系统会打印如下信息:
# insmod first_drv.ko # ./firstdrvtest on s3c2410_timer_interrupt : pid = 770, task_name = firstdrvtest s3c2410_timer_interrupt : pid = 770, task_name = firstdrvtest
根据上述信息可知,发送僵死的进程号为:770,发送僵死的进程名称为:firstdrvtest
3、继续完善,增加PC值,更加详细的定位僵死的地方我们知道,当中断发送的时候,在汇编中会调用asm_do_irq函数,
.macro irq_handler get_irqnr_preamble r5, lr 1: get_irqnr_and_base r0, r6, r5, lr movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ adrne lr, 1b bne asm_do_IRQ #调用C语言的函数
asm_do_IRQ 函数原型:
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { static pid_t pre_pid; static int cnt=0; struct pt_regs *old_regs = set_irq_regs(regs); struct irq_desc *desc = irq_desc + irq; /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (irq >= NR_IRQS) desc = &bad_irq_desc; irq_enter(); desc_handle_irq(irq, desc); /* AT91 specific workaround */ irq_finish(irq); irq_exit(); set_irq_regs(old_regs); }