跟着嵌入式Linux体系的广泛运用,对体系的可靠性提出了更高的要求,尤其是触及到生命财产等重要范畴,要求体系到达安全完整性等级3级以上 ,毛病率(每小时呈现风险毛病的或许性)为10-7以下,相当于体系的均匀毛病间隔时刻(MTBF)至少要到达1141年以上,因而进步体系可靠性已成为一项艰巨的使命。对某公司在工业范畴14 878个控制器体系的运用查询标明,从2004年初到2007年9月底,跟着硬软件的不断改善,依据过错报告计算的毛病率已降低到2004年的五分之一以下,但查找过错的时刻却增加到本来的3倍以上。
这种处理问题所需时刻呈上升的趋势固然有软件问题,但缺少必要的手法以辅佐处理问题才是首要的原因。经过对毛病的计算盯梢发现,难以处理的软件过错和从发现到处理耗时较长的软件过错都会集在操作体系的中心部分,这其间又有很大份额会集在驱动程序部分 。因而,过错盯梢技能被看成是进步体系安全完整性等级的一个重要措施 ,大多数现代操作体系均为开展供给了操作体系内核“溃散转储”机制,即在软件体系宕机时,将内存内容保存到磁盘 ,或许经过网络发送到毛病服务器 ,或许直接发动内核调试器 等,以供过后剖析改善。
根据Linux操作体系内核的溃散转储机制近年来有以下几种:
(1) LKCD(Linux Kernel Crash Dump)机制 ;
(2) KDUMP(Linux Kernel Dump)机制 ;
(3) KDB机制 ;
(4) KGDB机制 。
归纳上述几种机制能够发现,这四种机制之间有以下三个共同点:
(1) 适用于为运算资源丰富、存储空间足够的运用场合;
(2) 产生体系溃散后康复时刻无严格要求;
(3) 首要针对较通用的硬件渠道,如X86渠道。
在嵌入式运用场合想要直接运用上列机制中的某一种,却遇到以下三个难点无法处理:
(1) 存储空间缺乏
嵌入式体系一般选用Flash作为存储器,而Flash容量有限,且或许远远小于嵌入式体系中的内存容量。因而将悉数内存内容保存到Flash不行行。
(2) 记载时刻要求尽量短
嵌入式体系一般有复位呼应时刻尽量短的要求,有的嵌入式操作体系复位重启时刻不超越2s,而上述几种可用于Linux体系的内核溃散转储机制耗时均不或许在30s内。写Flash的操作也很耗时刻,试验显现,写2MB数据到Flash耗时到达400ms之多。
(3) 要求能够支撑特定的硬件渠道
嵌入式体系的硬件多种多样,上面说到的四种机制均是针对X86渠道供给了较好的支撑,而关于其他体系的硬件支撑均不老练。
因为这些难点的存在,要将上述四种内核溃散转储机制中的一种移植到特定的嵌入式运用渠道是好不容易的。因而,针对上述嵌入式体系的三个特色,本文介绍一种根据特定渠道的嵌入式Linux内核溃散信息记载机制LCRT(Linux Crash Record and Trace),为定位嵌入式Linux体系中软件毛病和处理软件毛病供给辅佐手法。
1 Linux内核溃散的剖析
剖析Linux内核关于运转期间各种“圈套”的处理能够得知,Linux内核关于运用程序导致的过错能够予以监控,在运用程序产生除零、内存拜访越界、缓冲区溢出等过错时,Linux内核的反常处理例程能够对这些由运用程序引起的反常情况予以处理。当运用程序产生不行康复的过错时,Linux内核能够只是停止产生过错的运用程序,其他运用程序依然能够正常运转。
假如Linux内核自身或许新开发的Linux内核模块存在bug,产生了“除零”,“内存拜访越界”、“缓冲区溢出”等过错,相同会由Linux内核的反常处理例程来处理。Linux内核经过在反常处理程序中判别,假如发现是“严峻的不行康复”的内核反常,则会导致“内核惊惧”(kernel panic),即Linux内核溃散。图1所示为Linux内核对反常情况的处理流程。
2 LCRT机制的规划与完结
经过对Linux内核代码的剖析可知,Linux内核自身供给了一种“内核告诉机制” ,并预界说了“内核事情告诉链”,使得Linux内核扩展开发人员能够经过这些预界说的内核事情告诉链在特定的内核事情产生时履行附加的处理流程。经过对Linux内核源代码的研讨发现,关于上文中说到的“严峻不行康复的内核反常”,预界说了一个告诉链和告诉点,使得在产生Linux内核溃散之后,能够在Linux内核的panic函数中预界说的一个“内核溃散告诉链” 上挂接LCRT机制来获得Linux内核溃散现场的一些信息并记载到非易失性存储器中,以便剖析引起Linux内核溃散的原因。
2.1 规划要害
LCRT机制的规划和完结根据如下特定的机制:
(1) 编译器选项与内核依靠
Linux内核及相应的驱动程序都选用GNU 的开源编译器GCC 编译,为了结合LCRT机制方便地提取信息和记载信息,需求选用特定的GCC编译器选项来编译Linux内核和相关的驱动程序以及运用程序。用到的选项为:-mpoke-function-name 。运用这个选项编译出的二进制程序中能够包含C言语函数称号的信息,以方便函数调用链回溯时记载信息的可读性。
(2) Linux内核noTIfy_chain机制
Linux内核供给“告诉链”功用,并预界说了一个内核溃散告诉链,在Linux内核的反常处理例程中判别出体系进入“不行康复”状况时,会沿预界说的告诉链顺序调用注册到相应链中的告诉函数。
(3) 函数调用的栈布局
Linux内核的绝大部分由C言语完结,并且C言语也多用来进行Linux内核开发。Linux内核及运用LKM扩展而参加Linux内核履行环境的代码是有规则可循的,这些代码在履行过程中产生的栈布局和这些规则的代码相关联。例如,这些函数在履行函数之前会保存本函数调用后的回来地址、本函数被调用时传递过来的参数及调用本函数的函数所具有的栈帧的栈底。
2.2 LCRT机制的规划思维
LCRT机制分为Linux内核模块 部分和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载入Linux内核,使得会引起Linux内核溃散的中止处理程序得以运转,LCRT机制能够将相关的信息保存到非易失性存储器中,在体系复位后,经过LCRT机制的用户空间东西,能够将保存的信息读取出来。试验成果显现,能够得到如图2所示的函数调用链信息。
图2标示即为会引起Linux内核溃散的过错代码的中止处理函数即真实引起体系宕机的“元凶巨恶”。而记载下的一切信息只是占用了不到1KB的存储空间,写入非易失性存储器所耗用的时刻控制在50ms以内。在运用少数空间和少数时刻的情况下,所记载下的信息关于查找问题和处理问题都有较大的协助。
试验成果标明,在LCRT机制的效果下,能够快速地定位到嵌入式Linux体系中躲藏的或许会导致体系宕机的软件缺陷。这就为后续的毛病处理和软件完善供给了要害的辅佐信息。对嵌入式Linux内核而言,便是为进步Linux内核的稳定性和可靠性供给了协助。
在根据ARM的嵌入式Linux运用中,开发LCRT机制来记载体系内核产生溃散时引起溃散的函数调用链和栈信息到非易失性存储器中,到目前为止,LCRT机制能够记载根据ARM的嵌入式Linux内核产生溃散时的函数调用链信息,可直接得到函数称号、函数调用链中单个函数被调用时的参数信息以及函数调用链中的函数各自的栈帧信息。这些记载下来的信息关于完善和开展根据ARM的嵌入式Linux运用具有重要的辅佐含义。