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信息会被多复制一份到黑匣子内存中。系统重启以后再把内存黑匣子中的信息转储到磁盘。

1

上图是系统保留内存的使用分布图,其中section PRINTK1和section PRINTK2部分被分配给printk黑匣子使用。

下面看一下具体的代码实现:

2
3

1.1、printk保留内存

4

1.2、注册console

注册console的主要目的是将printk打印,打印一份到保留内存的当前内存中去。

5
6
7
8

1.2.1、Kbox_DumpPanicInfor()

除了通过g_stPrintkConsole打印printk信息到printk黑匣子区域,还可以通过Kbox_DumpPanicInfor()和kbox_dump_printf ()直接打印内容到printk黑匣子区域。同时会打印内容到panic info区域的缓存g_aucSysLogBuf。

9

1.2.2、kbox_dump_printf ()

10

1.3、注册proc文件

注册proc文件的主要目的是将保留内存的上一次内存中的打印信息通过proc文件呈现。

11
12
13
14
15
16

2、dump设备

为了定位系统的异常原因,我们在各种异常处理的函数中注册自己的钩子函数,在出现异常时把定位信息dump到黑匣子设备中。

2.1、ram dump设备

系统的保留内存除了其中两块区域给printk重定向使用以外,还有其他的区域注册成字符设备,给dump信息使用。

上图是系统保留内存的使用分布图,其中:

17

  • 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()接口提供给用户访问。

    18
    19
    20
    21
    22
    23

2.1.1、stRamDevOp->pfnDevInit()

stRamDevOp->pfnDevInit ()在字符设备初始化函数KBox_RegisterDev()中被调用。

24
25
26

2.1.2、stRamDevOp-> pfnImgInit()

stRamDevOp-> pfnImgInit ()在字符设备初始化函数KBox_RegisterDev()中被调用。

27
28
29

2.1.3、stRamDevOp-> pfnImgInitWritePanicInfo()

stRamDevOp-> pfnImgInitWritePanicInfo ()在Kbox_WriteDumpLogToDev ()中被调用。

30
31
32

2.1.4、tRamDevOp-> pfnImgWritePanicInfo()

stRamDevOp-> pfnImgWritePanicInfo ()在Kbox_WriteDumpLogToDev ()中被调用。

33
34

2.1.5、stRamDevOp-> pfnImgWriteThreadInfo()

stRamDevOp-> pfnImgWriteThreadInfo ()在Kbox_WriteThreadInfor ()中被调用。

35
36
37

2.1.6、stRamDevOp-> pfnUserRegionOp()

stRamDevOp-> pfnUserRegionOp ()在KBOX_IOCtrl ()中被调用,用于用户态记录信息到section USER保留内存中。

38
39
40
41
42
43
44
45
46
47
48

2.1.7、stRamDevOp-> pfnWrite()

stRamDevOp-> pfnWrite ()在Kbox_Write ()中被调用,是字符设备真正的写函数。

49
50
51
52
53

2.1.8、stRamDevOp-> pfnRead()

stRamDevOp-> pfnRead ()在Kbox_Read ()中被调用,是字符设备真正的读函数。

54
55
56
57

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()

58
59
60
61
62
63

可以看到console口实质就是把printk内容打到g_aucSysLogBuf缓存中。

2.3.2、Kbox_DumpPanicInfor()

除了通过g_stKboxConsole打印printk信息到g_aucSysLogBuf缓存,还可以通过Kbox_DumpPanicInfor()和kbox_dump_printf ()直接打印内容到g_aucSysLogBuf缓存中。同时会打印内容到printk黑匣子区域。

64

2.3.3、kbox_dump_printf()

65

2.3.4、panic info dump

Kbox_OutputDumpLog负责将panic info缓存g_aucSysLogBuf中的数据刷新到真正的设备panic info区域中,并产生一条新的panic info记录。

66
67
68

2.4、thread info dump

Kbox_DumpThreadInfor()函数用来写入数据到设备的thread info区域。

69
70
71

Kbox_WriteThreadInforToDev()通过调用KBOX_PRINTK()来调用Kbox_DumpThreadInfor(),打印具体的线程信息到设备thread info区域。

72
73
74
75
76
77
78
79
80

3、异常dump

为了定位系统的异常原因,我们在各种异常处理的函数中注册自己的钩子函数,在出现异常时把定位信息dump到黑匣子设备中。

81

3.1、kbox_netnf_init()

注册网卡事件的钩子函数,在网卡up、down时通过printk打印日志,dump内核调用栈和进程间调用关系。

82
83
84
85
86

3.2、KBOX_RegisterPanicHook ()

87
88
89
90
91
92
93

3.3、KBOX_RegisterRebootHook ()

94
95
96
97
98
99
100

3.4、KBOX_RegisterMRestartHook()

101
102
103
104
105
106

3.5、KBOX_RegisterSecurityHook ()

107
108
109
110
111
112
113
114
115
116
117

3.6、KBOX_RegisterKprobeHook ()

118

3.6.1、stJpPanic

119

3.6.2、stJpButton

120
121
122
123

3.6.3、stJpMceError

124
125
126
127

3.6.4、ip、路由等事件监控

128
129
130

3.7、KBOX_RegisterBmcResetHook()

注册钩子函数来响应bmc的复位预中断。

131
132
133
134
135
136
137
138
139
140

3.8、KBOX_RegisterDogMonHook()

创建看门狗的监控进程,在看门狗复位之前预先记录信息。

141
142
143
144
145
146
147

3.9、Oom钩子函数

Oom全称out of memory即为系统内存耗尽,在系统本身的oom处理中,系统会杀死一些进程来缓解内存耗尽的情况。

148
149

在嵌入式系统中,如果出现oom估计也没有可以挽救的机会聊,可以在oom的钩子函数中注册自己的oom处理策略,直接panic系统。

150
151
152
153
154
155
156
157
158
159

4、死锁检测

除了系统可以自己侦测到的故障,很多故障是系统自己无法检测到的,在smp系统中我们可以使用多个cpu来相互检测异常,检测进程挂死的异常。

160
161
162
163
164
165
166
167

4.1、cpu死锁

168
169
170
171
172

4.2、进程死锁

173
174
175
176
177
178

“task->last_ran”任务最后一次运行时间戳是在任务调度函数schedule()中被设置的:

179

Logo

更多推荐