这些年曾经遇到过一些比较棘手的linux 异常问题,遗憾的是很多时候没有及时更新总结,导致我自己再次遇到的时候也要靠网络搜索相关文章去回忆解决办法,本文会持续更新,逐步完善linux 崩溃异常解决问题办法。
linux 崩溃异常无非是死锁,空指针,访问非法区域等问题,通常一般的方法,用静态的分析方式可以解决问题,实在比较复杂的情况只能用KGDB的等方式来解决。
为了方便调试和测试代码,内核提供了许多与内核调试相关的配置选项。这些选项大部分都在内核配置编辑器的内核开发(kernel hacking)菜单项中。在内核配置目录树菜单的其他地方也还有一些可配置的调试选项,下面将对他们作一定的介绍。
Page alloc debugging :CONFIG_DEBUG_PAGEALLOC:
不使用该选项时,释放的内存页将从内核地址空间中移出。使用该选项后,内核推迟移出内存页的过程,因此能够发现内存泄漏的错误。
Debug memory allocations :CONFIG_DEBUG_SLAB:
该打开该选项时,在内核执行内存分配之前将执行多种类型检查,通过这些类型检查可以发现诸如内核过量分配或者未初始化等错误。内核将会在每次分配内存前后 时设置一些警戒值,如果这些值发生了变化那么内核就会知道内存已经被操作过并给出明确的提示,从而使各种隐晦的错误变得容易被跟踪。
Spinlock debugging :CONFIG_DEBUG_SPINLOCK:
打开此选项时,内核将能够发现spinlock未初始化及各种其他的错误,能用于排除一些死锁引起的错误。
Sleep-inside-spinlock checking:CONFIG_DEBUG_SPINLOCK_SLEEP:
打开该选项时,当spinlock的持有者要睡眠时会执行相应的检查。实际上即使调用者目前没有睡眠,而只是存在睡眠的可能性时也会给出提示。
Compile the kernel with debug info :CONFIG_DEBUG_INFO:
打开该选项时,编译出的内核将会包含全部的调试信息,使用gdb时需要这些调试信息。
Stack utilization instrumentation :CONFIG_DEBUG_STACK_USAGE:
该选项用于跟踪内核栈的溢出错误,一个内核栈溢出错误的明显的现象是产生oops错误却没有列出系统的调用栈信息。该选项将使内核进行栈溢出检查,并使内核进行栈使用的统计。
Driver Core verbose debug messages:CONFIG_DEBUG_DRIVER:
该选项位于"Device drivers-> Generic Driver Options"下,打开该选项使得内核驱动核心产生大量的调试信息,并将他们记录到系统日志中。
Verbose SCSI error reporting (kernel size +=12K) :CONFIG_SCSI_CONSTANTS:
该选项位于"Device drivers/SCSI device support"下。当SCSI设备出错时内核将给出详细的出错信息。
Event debugging:CONFIG_INPUT_EVBUG:
打开该选项时,会将输入子系统的错误及所有事件都输出到系统日志中。该选项在产生了详细的输入报告的同时,也会导致一定的安全问题。
以上内核编译选项需要读者根据自己所进行的内核编程的实际情况,灵活选取。在使用以上介绍的三种源代码级的内核调试工具时,一般需要选取CONFIG_DEBUG_INFO选项,以使编译的内核包含调试信息。
1、空指针等导致的oops等异常
关键异常信息如下:
[2020/8/3 14:56:16] [ 194.829793] [<ffffff800854008c>] _regulator_disable+0xc4/0x198
[2020/8/3 14:56:16] [ 194.829797] [<ffffff80085401b8>] regulator_disable+0x58/0x88
[2020/8/3 14:56:16] [ 194.829801] [<ffffff80086dd0f0>] __sunxi_set_vbus+0x100/0x1a0
[2020/8/3 14:56:16] [ 194.829805] [<ffffff80086dd43c>] sunxi_set_vbus+0x2ac/0x2c8
[2020/8/3 14:56:16] [ 194.829810] [<ffffff80086c7df0>] sunxi_stop_ehci+0x28/0x58
[2020/8/3 14:56:16] [ 194.829813] [<ffffff80086cfe7c>] sunxi_ehci_hcd_suspend+0x94/0x238
[2020/8/3 14:56:16] [ 194.829818] [<ffffff80085c1ba0>] platform_pm_suspend+0x38/0x70
[2020/8/3 14:56:16] [ 194.829821] [<ffffff80085cfb40>] dpm_run_callback+0x58/0x208
[2020/8/3 14:56:16] [ 194.829824] [<ffffff80085d0df8>] __device_suspend+0x168/0x450
[2020/8/3 14:56:16] [ 194.829828] [<ffffff80085d1114>] async_suspend+0x34/0xb0
[2020/8/3 14:56:16] [ 194.829831] [<ffffff80080d4504>] async_run_entry_fn+0x44/0x160
[2020/8/3 14:56:16] [ 194.829835] [<ffffff80080c9828>] process_one_work+0x150/0x4b0
[2020/8/3 14:56:16] [ 194.829838] [<ffffff80080c9cc4>] worker_thread+0x13c/0x4c8
[2020/8/3 14:56:16] [ 194.829842] [<ffffff80080d06cc>] kthread+0xec/0x100
[2020/8/3 14:56:16] [ 194.829846] [<ffffff80080834f0>] ret_from_fork+0x10/0x20
从代码上来看,应该是最终调用到_regulator_disable 函数出了问题,这种问题可以用gdb分析vmlinux文件找到出问题的点
(gdb) l*(_regulator_disable+0xc4)
0xffffff800854008c is in _regulator_disable (drivers/regulator/core.c:2783).
2778 {
2779 int ret = 0;
2780
2781 lockdep_assert_held_once(&rdev->mutex);
2782
2783 if (WARN(rdev->use_count <= 0,
2784 "unbalanced disables for %s\n", rdev_get_name(rdev)))
2785 return -EIO;
2786
2787 /* are we the last user and permitted to disable ? */
应该是 rdev_get_name(rdev)) 出了问题,很明显这是指针原因造成的,那这个指针是从上层传入的,
那通过gdb再找上层代码在哪里
(gdb) l*(regulator_disable+0x58)
0xffffff80085401b8 is in regulator_disable (drivers/regulator/core.c:2851).
2846 }
2847 axp_del_enabler(rdev, regulator->supply_name);
2848 #endif
2849
2850 mutex_lock(&rdev->mutex);
2851 ret = _regulator_disable(rdev);
2852 mutex_unlock(&rdev->mutex);
2853
2854 if (ret == 0 && rdev->supply)
2855 regulator_disable(rdev->supply);
(gdb)
这还不是源头,并且这个是linux 内核自带的代码,不适合在这里修改,继续往上找。
最终找到是因为休眠的时候调用sunxi_ehci_hcd_suspend函数之后调用sunxi_stop_ehci(sunxi_ehci)传入的指针有问题才会导致异常的,对指针做个IS_ERR_OR_NULL就可以解决问题了。
以前曾经遇到过一个一两天才出现内核自旋锁锁死的问题,等以后遇到再分析了,不过这个静态gdb分析类似方法是可以很快找到问题的,这些内核错误信息给了我们很大帮助。
本文汇总了Linux内核异常如空指针导致的Oops等问题的分析与解决方法,包括使用gdb进行动态分析,以及如何从内核错误信息中定位问题。举例介绍了在_sunxi_set_vbus_函数中因指针问题引发的异常,强调静态分析和IS_ERR_OR_NULL检查的重要性。
16万+

被折叠的 条评论
为什么被折叠?



