嵌入式Linux Kernel错误跟踪技术
相关的驱动程序以及应用程序。用到的选项为:-mpoke-function-name[9]。使用这个选项编译出的二进制程序中可以包含C语言函数名称的信息,以方便函数调用链回溯时记录信息的可读性。
(2) Linux内核notify_chain机制[8]
Linux内核提供"通知链"功能,并预定义了一个内核崩溃通知链,在Linux内核的异常处理例程中判断出系统进入"不可恢复"状态时,会沿预定义的通知链顺序调用注册到相应链中的通知函数。
(3) 函数调用的栈布局
Linux内核的绝大部分由C语言实现,而且C语言也多用来进行Linux内核开发。Linux内核及使用LKM扩展而加入Linux内核执行环境的代码是有规律可循的,这些代码在执行过程中产生的栈布局和这些规律的代码相关联。例如,这些函数在执行函数之前会保存本函数调用后的返回地址、本函数被调用时传递过来的参数及调用本函数的函数所拥有的栈帧的栈底。
2.2 LCRT机制的设计思想
LCRT机制分为Linux内核模块[8]部分和Linux用户程序部分。内核模块部分的设计采用了Linux内核模块的模式而不是直接修改Linux内核。这样的设计降低了Linux内核和LCRT机制之间的耦合度,同时满足了Linux内核和LCRT机制独立升级完善的便利性。用户程序部分完成从非易失性存储器中读取、清除LCRT机制保存的信息等相关功能。
在LCRT机制的设计中,针对嵌入式系统的特点,其设计决策有:
(1) 将对于解决和定位问题最具辅助意义的函数调用关系链记录下来。
(2) 为了不占用过多的存储空间,有选择性地将函数调用序列上的函数各自用到的栈内容保存起来,而不是保存全部内容。
(3) 将记录的信息保存到非易失性存储器中,这样既达到了掉电保存的目的、又缩短了写入时间。
LCRT机制的设计包括以下五个方面。
(1) 设计Linux内核模块、动态地加载LCRT机制、尽量少地修改Linux内核代码。
(2)在相应、预定义的Linux内核通知链上挂接LCRT的通知函数。
(3) 在LCRT机制的通知处理函数中进行堆栈回溯得到函数调用信息。
(4) 记录回溯到的函数调用信息和堆栈空间内容到非易失性存储器。
(5) 开发用户空间的工具,可以从非易失性存储器中读取保存的信息。
2.3 LCRT机制的实现
LCRT机制的实现可参照2.2节的设计思想,分步予以实现。限于篇幅,本文不过多涉及Linux内核模块的原理和实现相关的细节,仅仅给出LCRT机制的内核模块实现伪代码。用伪代码描述LCRT机制的加载函数如下:
int lcrt_init(void)
{
printk("Registering my__panic notifier.\n");
bt_nvram_ptr=(volatile unsigned char*)ioremap_
nocache (BT_NVRAM_BASE,BT_NVRAM_LENGTH);
bt_nvram_index+=sizeof(struct bt_info);
*)bt_nvram_ptr,BT_NVRAM_LENGTH);
notifier_chain_register(&panic_notifier_list,&my_
panic_block);
return 0;
}
LCRT机制的通知处理函数完成函数调用关系回溯、得到函数名称、函数栈内容等工作,限于篇幅,在这里用下面伪代码说明:
void ll_bt_information(struct pt_regs *pr)
{
变量定义等初始化工作
do {
reglist=*(unsigned long *)(*myfp-8);
//从函数栈帧的顶部获取函数开始执行时保存的寄存器信息
//从函数的代码区中取得函数的名称
//从函数的栈帧里取出函数执行函数体代码之前保存的函数参数信息
//从本函数的栈帧中得到调用本函数的代码所在位置和调用本函数的函数栈帧的栈底
}while(直到函数调用链的链头);
//取得函数调用栈帧的内容
//填充信息记录的记录头部
//将上面的循环中取得的信息保存到非易失性存储器中
write_to_nvram((void *)bt_nvram_ptr,&bt_record_header,sizeof(bt_info_t));
}
3 验证评估LCRT机制
3.1 部署LCRT机制
部署LCRT机制,使LCRT机制发挥作用前需要做的相关工作有:
(1)针对目标Linux内核编译LCRT机制的Linux内核模块部分;
(2) 将LCRT机制的内核模块部分载入Linux内核。
3.2 实验结果
为了实验LCRT机制的作用效果,构造一个会造成Linux内核崩溃的设备驱动模块,记这个内核驱动模块为bugguy.ko,列出如下所示的bugguy.ko中会引起Linux内核崩溃的代码如下所示:
irqreturn_t my_timer_interrupt(int irq,void *dev_id,struct pt_regs* regs)
{
确认硬件状态并清除中断状态
if(ujiffies > 5000) {
void * ill_pointer=NULL;
*(unsigned long *)ill_pointer=0;
}
else {
ujiffies++;
}
return IRQ_HANDLED;
}
说明:用黑体标出的代码即为产生bug的代码
从上面的代码可以看出,这个错误是对空指针进行解析而造成的。在一个中断处理函数中如果发生对空指针的解析,将会引起Linux内核的崩溃。在部署完成LCRT机制的嵌入式linux系统上将这个bugguy.ko载入L
- REDIce-Linux--灵活的实时Linux内核(11-12)
- linux文件系统基础(02-09)
- Linux标准趋向统一(11-12)
- linux基础技术(02-09)
- LINUX的目录树(02-09)
- 在Windows下启动Linux(02-09)