论文部分内容阅读
摘 要:在嵌入式产品中,经常有一些现场频发但是又难以还原现场的异常问题,为了更好的处理这类问题,保持异常问题发生时的现场环境,本文提出了一种内存转储日志方案,可以很好的保存现场日志,为日后分析提供完善的日志资料。
关键词:嵌入式系统; 内存转储; 日志系统;
问题背景
通常嵌入式产品一般都配有一个flash存储器,日志系统会实时的把系统的一些运行状况和关键事件写入到Flash文件系统中的日志文件。
由于异常本身在实验环境下难以复现,而在现场问题复现时,日志线程或Flash文件系统已经被破坏,导致无法获得具有分析价值的现场日志。
为了解决这个问题,同时为后续的产品提供一种更健壮的日志系统,我们设计了该内存转储日志系统。
设计原理
对于嵌入式设备来说,内存系统是最小系统的基础单元,比其它的外部设备要相对稳定,所以我们首先考虑的就是把日志信息写到内存中。再把存储在内存中的日志内容,完整的回写到Flash中。
首先我们知道内存芯片不掉电,其存储的内容是不会丢失的。如果缓存日志的内存地址是一块固定且不会被Linux系统破坏的内存空间,那么在重启时,就可以安全的把其中的内存保存下来。
预留内存
在uboot启动时,可以通过mem参数可以给linux系统传递内存大小,通过设置mem参数,预留5M的内存空间。在驱动程序中,我们把这未分配给系统的内存进行映射:
Request_mem_region(max_mapnr< 这样就可以通过/proc/iomem参数查看到我们为日志系统预留的内存了
内存分布结构
预留的内存被换简单的划分为三部分,分别是Header、console日志区、异常信号日志区。
Head头
Struct LOG_SYS_HEADER
{
unsigned int magic;
unsigned int size;
}
magic魔术字,例如0x9B9B9B9B。该字段可以作为内存段数据是否有效的标识,如果魔术字不匹配,表示该段内存已经被破坏。
结构体成员size表示保留内存区总大小。
Console日志区
Console日志区,由于现场的环境比较复杂,难以通过串口直接对设备进行调试,所以保存设备最后复位状态前的console输出信息就显得非常有必要。Console日志区是用来同步保存标准输出的活动记录信息。
异常信号日志区
在linux系统大部分的软件异常都表现为异常信号,所以我们专门开辟了一个异常信号日志区,用来存放捕获信号量时的堆栈信息,当系统发生段错误、指令错误、地址总线错误等系统异常时,会把发生异常时的现场信息保存到该区。
日志存储
异常信号处理函数挂载。
为了保持异常发生时的现场信息到日志区,注册了LinuxSignalHandler()函数作为大部分异常信号的处理函数。当系统发生段错误、指令错误、地址总线错误等系统异常时,会触发信号处理函数,从而记录下现场信息以供事后分析。
当异常信号触发时,LinuxSignalHandler()信号处理函数被调用,保存一些异常基本信息到异常信号日志区,包括:当前CPU所有通用寄存器和其他重要寄存器值、进程ID、线程ID、异常信号、Linux内核异常信号码、堆栈信息等。
Cosole日志重定向。
Console日志是由一个监护进程,实时的读取标准输出,并把输出写到console日志内存区。
其原理图如下:
日志回写
当异常发生时,触发看门狗,引发复位。设备重新启动。
在设备启动过程中,会首先检查预留内存区域中Header头部的魔术字,如果不相等,表明预留内存的数据结构已经被破坏,其中的内容无效。
如果魔术字校验正确,待Flash等外设初始化完成后,则把其中的内容拷贝出来,保存到Flash存储器上。清空保留内存,以待下一次日志存储。
结束语
该方案在现实应用中,帮我们成功的捕获了前文提到的异常日志,从而成功的定位到了发生异常的原因。同时在后来的实际应用中,对一些现场频发的,难以现场还原的棘手问题都提供了非常好的辅助效果。
关键词:嵌入式系统; 内存转储; 日志系统;
问题背景
通常嵌入式产品一般都配有一个flash存储器,日志系统会实时的把系统的一些运行状况和关键事件写入到Flash文件系统中的日志文件。
由于异常本身在实验环境下难以复现,而在现场问题复现时,日志线程或Flash文件系统已经被破坏,导致无法获得具有分析价值的现场日志。
为了解决这个问题,同时为后续的产品提供一种更健壮的日志系统,我们设计了该内存转储日志系统。
设计原理
对于嵌入式设备来说,内存系统是最小系统的基础单元,比其它的外部设备要相对稳定,所以我们首先考虑的就是把日志信息写到内存中。再把存储在内存中的日志内容,完整的回写到Flash中。
首先我们知道内存芯片不掉电,其存储的内容是不会丢失的。如果缓存日志的内存地址是一块固定且不会被Linux系统破坏的内存空间,那么在重启时,就可以安全的把其中的内存保存下来。
预留内存
在uboot启动时,可以通过mem参数可以给linux系统传递内存大小,通过设置mem参数,预留5M的内存空间。在驱动程序中,我们把这未分配给系统的内存进行映射:
Request_mem_region(max_mapnr<
内存分布结构
预留的内存被换简单的划分为三部分,分别是Header、console日志区、异常信号日志区。
Head头
Struct LOG_SYS_HEADER
{
unsigned int magic;
unsigned int size;
}
magic魔术字,例如0x9B9B9B9B。该字段可以作为内存段数据是否有效的标识,如果魔术字不匹配,表示该段内存已经被破坏。
结构体成员size表示保留内存区总大小。
Console日志区
Console日志区,由于现场的环境比较复杂,难以通过串口直接对设备进行调试,所以保存设备最后复位状态前的console输出信息就显得非常有必要。Console日志区是用来同步保存标准输出的活动记录信息。
异常信号日志区
在linux系统大部分的软件异常都表现为异常信号,所以我们专门开辟了一个异常信号日志区,用来存放捕获信号量时的堆栈信息,当系统发生段错误、指令错误、地址总线错误等系统异常时,会把发生异常时的现场信息保存到该区。
日志存储
异常信号处理函数挂载。
为了保持异常发生时的现场信息到日志区,注册了LinuxSignalHandler()函数作为大部分异常信号的处理函数。当系统发生段错误、指令错误、地址总线错误等系统异常时,会触发信号处理函数,从而记录下现场信息以供事后分析。
当异常信号触发时,LinuxSignalHandler()信号处理函数被调用,保存一些异常基本信息到异常信号日志区,包括:当前CPU所有通用寄存器和其他重要寄存器值、进程ID、线程ID、异常信号、Linux内核异常信号码、堆栈信息等。
Cosole日志重定向。
Console日志是由一个监护进程,实时的读取标准输出,并把输出写到console日志内存区。
其原理图如下:
日志回写
当异常发生时,触发看门狗,引发复位。设备重新启动。
在设备启动过程中,会首先检查预留内存区域中Header头部的魔术字,如果不相等,表明预留内存的数据结构已经被破坏,其中的内容无效。
如果魔术字校验正确,待Flash等外设初始化完成后,则把其中的内容拷贝出来,保存到Flash存储器上。清空保留内存,以待下一次日志存储。
结束语
该方案在现实应用中,帮我们成功的捕获了前文提到的异常日志,从而成功的定位到了发生异常的原因。同时在后来的实际应用中,对一些现场频发的,难以现场还原的棘手问题都提供了非常好的辅助效果。