[monitor] 7. Linux几种内核故障定位方法
Linux系统崩溃有kdump机制来记录,但是由于kdump的文件内容一般很大,在嵌入式系统一般不使用,嵌入式系统一般使用把printk重定位到黑匣子中的方法来记录异常,另外还需要其他的故障检测方法来完善系统所有场景下的异常检测。除了系统的一些通用方法,我们在定位系统死机、复位故障时需要更多的手段来搜集信息,一般嵌入式系统采取的手段有:1、创建内存、flash黑匣子,把故障点、函数调用栈等信息在复
Linux系统崩溃有kdump机制来记录,但是由于kdump的文件内容一般很大,在嵌入式系统一般不使用,嵌入式系统一般使用把printk重定位到黑匣子中的方法来记录异常,另外还需要其他的故障检测方法来完善系统所有场景下的异常检测。
除了系统的一些通用方法,我们在定位系统死机、复位故障时需要更多的手段来搜集信息,一般嵌入式系统采取的手段有:
- 1、创建内存、flash黑匣子,把故障点、函数调用栈等信息在复位之前记录到黑匣子,以供第二次复位起来以后分析;
- 2、在各种可能产生复位的地方,包括各种主动复位和被动复位的地方(如:panic、reboot、按键复位、看门狗复位)加上钩子函数,记录下具体的复位原因;
- 3、一些主动监测死锁的手段。
1、printk黑匣子
在一些内核本身可以检测到的故障发生时,内核会陷入Panic、die、BUG_ON等函数,打印出故障定位信息和调用栈信息。
对于这一类的系统复位问题,除了kdump机制把系统内存信息记录下来以外,另一个可行的方法就是查看复位前的printk内核打印信息, 所以获得上一次复位前的printk信息对定位这一类问题非常有用,系统本身是通过klog和syslog进程记录printk信息到日志,但是在系统崩溃时这种机制不能保证信息能被记录下来。
通常的做法是定制黑匣子来记录printk信息,具体的原理就是保留一段系统热复位不会被清除的内存,注册一个新的console,printk信息会被多复制一份到黑匣子内存中。系统重启以后再把内存黑匣子中的信息转储到磁盘。
上图是系统保留内存的使用分布图,其中section PRINTK1和section PRINTK2部分被分配给printk黑匣子使用。
下面看一下具体的代码实现:
1.1、printk保留内存
1.2、注册console
注册console的主要目的是将printk打印,打印一份到保留内存的当前内存中去。
1.2.1、Kbox_DumpPanicInfor()
除了通过g_stPrintkConsole打印printk信息到printk黑匣子区域,还可以通过Kbox_DumpPanicInfor()和kbox_dump_printf ()直接打印内容到printk黑匣子区域。同时会打印内容到panic info区域的缓存g_aucSysLogBuf。
1.2.2、kbox_dump_printf ()
1.3、注册proc文件
注册proc文件的主要目的是将保留内存的上一次内存中的打印信息通过proc文件呈现。
2、dump设备
为了定位系统的异常原因,我们在各种异常处理的函数中注册自己的钩子函数,在出现异常时把定位信息dump到黑匣子设备中。
2.1、ram dump设备
系统的保留内存除了其中两块区域给printk重定向使用以外,还有其他的区域注册成字符设备,给dump信息使用。
上图是系统保留内存的使用分布图,其中:
- section PRINTK1和section PRINTK2部分被分配给printk黑匣子使用;
- section KERNEL被分成两部分记录panic info和thread info,分别使用Kbox_OutputDumpLog()和Kbox_DumpThreadInfor ()函数来呈现;
- section USER记录用户态的数据,通过字符设备的ioctl接口开放给用户使用;
section ALL包括所有的KERNEL、PRINTK1、PRINTK2、USER区域,通过字符设备的read()、write()接口提供给用户访问。
2.1.1、stRamDevOp->pfnDevInit()
stRamDevOp->pfnDevInit ()在字符设备初始化函数KBox_RegisterDev()中被调用。
2.1.2、stRamDevOp-> pfnImgInit()
stRamDevOp-> pfnImgInit ()在字符设备初始化函数KBox_RegisterDev()中被调用。
2.1.3、stRamDevOp-> pfnImgInitWritePanicInfo()
stRamDevOp-> pfnImgInitWritePanicInfo ()在Kbox_WriteDumpLogToDev ()中被调用。
2.1.4、tRamDevOp-> pfnImgWritePanicInfo()
stRamDevOp-> pfnImgWritePanicInfo ()在Kbox_WriteDumpLogToDev ()中被调用。
2.1.5、stRamDevOp-> pfnImgWriteThreadInfo()
stRamDevOp-> pfnImgWriteThreadInfo ()在Kbox_WriteThreadInfor ()中被调用。
2.1.6、stRamDevOp-> pfnUserRegionOp()
stRamDevOp-> pfnUserRegionOp ()在KBOX_IOCtrl ()中被调用,用于用户态记录信息到section USER保留内存中。
2.1.7、stRamDevOp-> pfnWrite()
stRamDevOp-> pfnWrite ()在Kbox_Write ()中被调用,是字符设备真正的写函数。
2.1.8、stRamDevOp-> pfnRead()
stRamDevOp-> pfnRead ()在Kbox_Read ()中被调用,是字符设备真正的读函数。
2.2、flash dump设备
flash dump设备和ram dump设备的原理相同,只是记录的信息更少,这里就不详细解析。
2.3、panic info缓存
系统创建了一个临时panic info缓存即g_aucSysLogBuf,在需要刷新的时候才会把缓存内存刷新到ram、flash dump设备的panic info区域当中。一方面会把它注册成consle用来存储printk信息,另一方面也可以直接调用接口向缓存中打印。
2.3.1、注册console()
可以看到console口实质就是把printk内容打到g_aucSysLogBuf缓存中。
2.3.2、Kbox_DumpPanicInfor()
除了通过g_stKboxConsole打印printk信息到g_aucSysLogBuf缓存,还可以通过Kbox_DumpPanicInfor()和kbox_dump_printf ()直接打印内容到g_aucSysLogBuf缓存中。同时会打印内容到printk黑匣子区域。
2.3.3、kbox_dump_printf()
2.3.4、panic info dump
Kbox_OutputDumpLog负责将panic info缓存g_aucSysLogBuf中的数据刷新到真正的设备panic info区域中,并产生一条新的panic info记录。
2.4、thread info dump
Kbox_DumpThreadInfor()函数用来写入数据到设备的thread info区域。
Kbox_WriteThreadInforToDev()通过调用KBOX_PRINTK()来调用Kbox_DumpThreadInfor(),打印具体的线程信息到设备thread info区域。
3、异常dump
为了定位系统的异常原因,我们在各种异常处理的函数中注册自己的钩子函数,在出现异常时把定位信息dump到黑匣子设备中。
3.1、kbox_netnf_init()
注册网卡事件的钩子函数,在网卡up、down时通过printk打印日志,dump内核调用栈和进程间调用关系。
3.2、KBOX_RegisterPanicHook ()
3.3、KBOX_RegisterRebootHook ()
3.4、KBOX_RegisterMRestartHook()
3.5、KBOX_RegisterSecurityHook ()
3.6、KBOX_RegisterKprobeHook ()
3.6.1、stJpPanic
3.6.2、stJpButton
3.6.3、stJpMceError
3.6.4、ip、路由等事件监控
3.7、KBOX_RegisterBmcResetHook()
注册钩子函数来响应bmc的复位预中断。
3.8、KBOX_RegisterDogMonHook()
创建看门狗的监控进程,在看门狗复位之前预先记录信息。
3.9、Oom钩子函数
Oom全称out of memory即为系统内存耗尽,在系统本身的oom处理中,系统会杀死一些进程来缓解内存耗尽的情况。
在嵌入式系统中,如果出现oom估计也没有可以挽救的机会聊,可以在oom的钩子函数中注册自己的oom处理策略,直接panic系统。
4、死锁检测
除了系统可以自己侦测到的故障,很多故障是系统自己无法检测到的,在smp系统中我们可以使用多个cpu来相互检测异常,检测进程挂死的异常。
4.1、cpu死锁
4.2、进程死锁
“task->last_ran”任务最后一次运行时间戳是在任务调度函数schedule()中被设置的:
更多推荐
所有评论(0)