背景:
Linux嵌入式设备内核挂死后,无法自动重启,需要手动重启。而且如果当时没有连串口的话,就无法记录内核挂死时的堆栈,所以需要添加一种方式来记录内核挂死信息方便以后调试使用。设备中增加kdump功能,可以将内核挂死的堆栈信息记录下来,供后期分析。
操作步骤:
1、添加kdump所需的程序及配置文件
方法:目前所分析得知需要如下文件/sbin/kdump /sbin/kexec /bin/kdumpctl /etc/kdump.conf /etc/sysconfig/kdump,然后编辑etc目录下的两个配置文件;修改kdump中KDUMP_BOOTDIR="/mnt",KDUMP_COMMANDLINE_APPEND="1 irqpoll maxcpus=1",KDUMP_IMG="kernel.img";修改kdump.conf内容为path /mnt/workdir/log;还需要修改kdumpctl中有关kdump和kexec的路径;将这些文件放到设备的mnt目录下然后重启,重启后执行kexec -l /mnt/kernel.img --initrd=/mnt/rootfs.img --command-line="`cat /proc/cmdline` 1 irqpoll maxcpus=1";kexec -e 查看是否添加成功。
2、问题:各种工具、配置文件均添加成功,但是执行kdumpctl start命令时,依然报错 BUG: unable to handle kernel paging request at ffff880001497000,或者不打印堆栈信息直接提示killed。
解决方法:经过测试,将添加到grub.cfg中的crashkernel=128M@16M改成crashkernel=128M,完全省略“@Y”这一部分,这样,kernel 会为我们自动选择一个起始地址。这样就不会出现起始地址错误等问题了。在测试kexec时,最好加上-d选项,将debug信息打印出来。
3、问题:经测试,按照第二步中的操作方法,kexec -e执行完成后,内核打完堆栈之后并未重启,而是直接卡死了。
解决方法:分析之后才发现,因为在/etc/sysconfig/kdump配置文件KDUMP_COMMANDLINE_APPEND字段添加了reset_devices,所以导致kdump内核无法重启。将KDUMP_COMMANDLINE_APPEND选项内容中reset_devices这个值删掉就可以了。
4、问题:内核panic错误发生后,内核重启,但是只进入到了新解压后的rootfs中,而系统的inittab根本就未被执行,导致后续的脚本没有执行。
解决方法:经过查找相关资料发现,linux在内核加载完成后,会解压initrd成rootfs根文件系统,随后执行根文件系统中的linuxrc或者init来初始化inittab配置文件,然后进行一系列的脚本初始化操作。根据现象发现,inittab确实没有被执行过,所以就开始查找inittab未被执行的原因。然后修改/etc/sysconfig/kdump文件中的KDUMP_COMMANDLINE_APPEND的值为"1 irqpoll maxcpus=1 init=/linuxrc",然后就可以执行成功了。
5、问题:由于采用原有的rootfs会造成设备启动许多不必要的程序、服务,所以应该重新定制一个kdump专用的rootfs。
解决方法:下载新版的busybox,然后编译、安装生成_install目录,该目录下就是部分文件系统,然后在创建dev,etc,lib,lib64,var,proc,tmp,mnt,sys这些系统所需的目录以及设备所需的workdir,mnt目录。由于编译busybox时,采用静态编译出错,所以没有采用静态编译的方式,因而需要用ldd busybox查看busybox所需的动态库,然后将这些动态库拷贝到创建好的/lib64目录中。由于linuxrc需要初始化系统,因而需要在/etc目录下添加fstab、inittab以及init.d/rcS这三个必备文件,由于fstab和inittab只跟系统有关,所以可以借用原来rootfs的对应文件,rcS系统程序的初始化脚本,可以将自己所需要实现的命令添加到rcS中,还需要在dev目录中添加相关设备节点,用mknod命令添加console, null等设备以及shm,pts目录作为挂载点。由于kdump重启后的系统中生成的vmcore过大,CF卡存储不下,只能用vmcore-dmesg /proc/vmcore命令提取内核崩溃日志,所以rcS就简单实现了挂载文件系统、CF卡、提取内核崩溃信息,然后重启系统的功能。由于新的rootfs是在kdump过程中使用,与原有的rootfs独立,所以提取内核崩溃日志的相关程序就需要预先添加到新的rootfs相关的目录中。整个rootfs目录文件添加完成之后,就在_install目录下执行find . | cpio -H newc --quiet -o | gzip -9 >~/rootfs.img,将这些文件夹打包并压缩生成rootfs.img放到当前用户的home目录。
6、问题:kdumpctl启动kdump服务时,要求内核以及initrd的名称格式,而且需要修改boot.cfg启动配置文件。
解决方案:最开始向采用修改安装包的方式,向安装包中添加kdump需要的内核以及initrd文件,但是经过尝试无法将这些文件打到vsos.bin中,后来发现是package.ini文件的配置,修改package.ini的配置后,可以将对应的文件添加到vsos.bin中了。但是在设备安装的时候,依然没有安装新添加的文件。由于对设备解包安装的过程不太熟悉,所以就采取折衷的方式:将新的initrd(即kdump需要的rootfs文件)添加到/mnt/system目录下,然后在开机启动脚本start.sh中,添加修改boot.cfg的功能,并将新的rootfs文件移至/mnt目录下,同时新建一个对原有内核文件的软连接供kdump使用,由于kdumpctl会检查内核文件、initrd文件、以及kdump的配置文件的时间戳,如果initrd文件的时间戳早于其他文件,kdumpctl尝试重新生成initrd文件,这个过程中很有可能报错,所以在移动文件结束后,就会用touch命令更新initrd文件的时间到最新。
7、问题:CF卡的磁盘编号有时不是/dev/sda1,导致kdump重启后按照/dev/sda1挂载失败
解决方法:用这个命令fdisk -l | grep "83" | grep "\*" | grep dev | awk '{print $1}'来获取CF卡的设备号。
内容汇总
kexec -l选项是直接加载内核,然后配置kexec -e命令启动新内核;而kexec -p选项则是指明当前内核遇到panic时,要启用的内核。需要echo c>/proc/sysrq-trigger,手动触发内核panic。
其实完全可以用kexec取代kdumpctl,这样就可以直接用老的内核名称,而且也不用考虑对kernel、initrd及配置文件的时间戳问题,而且由于采用的是新的rootfs系统,通过修改rcS脚本自己手动实现的内核崩溃信息保存,完全可以脱离对kdump相关配置文件的依赖,这样做是最简单的。但是考虑到标准linux中启动kdump的方式兼容,方便后续人员维护,就采用了kdumpctl的方式启动kdump服务。
由于在svn上提交的是一个封装好的img文件,所以要想对修改img文件中的内容,则需要先将img文件拷贝到一个新的文件夹下,然后执行:mv rootfs.img rootfs.img.gz; gzip -d rootfs.img.gz; cpio -id 还原出整个文件系统,然后按照需求进行修改,随后执行find . | cpio -H newc --quiet -o | gzip -9 >~/rootfs.img重新生成img文件。上述重新生成img文件的命令中,最后img文件的位置不能在当前目录。
得失分析:
由于对内核相关内容不太熟悉,所以前期只能通过从网上找相关资料,一点点尝试。虽然这个过程难了点儿,但是自己从中还是学到了很多东西,诸如Linux启动过程的详细过程之类的,学会了裁剪内核以及busybox,可以搭建一个小的迷你linux。
由于时间限制,并未进一步实现提取vmcore的功能。不过在现在有工作的基础之上应该很容易添加获取vmcore的功能。