开发环境 -- Linux内核死锁检测

原文链接: https://blog.csdn.net/ccwzhu/article/details/81171092

问题描述

    实际产品运行过程中,Linux系统僵死,屏幕无任何有效串口打印信息,网络中断、键盘鼠标没有任何响应。
    这种故障现象,可能是因为Linux内核死锁导致。由于无任何有效打印信息,内核日志中也没有记录,就无法定位故障根因。
    
 如何让Linux内核在僵死前打印相关信息,对问题定位尤为关键。其中一个有效手段是打开“Kernel Hacking”选项,然后重新编译内核。对于Linux(3.14.28)内核死锁有帮助的几个配置选项有:

   Kernel hacking  --->Debug Lockups and Hangs  --->      

                               [*] Detect Hard and Soft Lockups                                                                                     
                               [*]   Panic (Reboot) On Soft Lockups             
                               [*] Detect Hung Tasks                                                                                                 
                              (120) Default timeout for hung task detection (in seconds)                                      
                               [*]   Panic (Reboot) On Hung Tasks   

lockdep 死锁检测模块
    介绍了最简单的 ABBA 死锁的形成,回到正题,回到 kernel, 里面有千千万万锁,错综复杂,也不可能要求所有开发人员熟悉 spin_lock, spin_lock_irq, spin_lock_irqsave, spin_lock_nested 的区别。所以,在锁死发生前,还是要做好预防胜于治疗,防患于未然的工作,尽量提前发现并且提前在开发阶段发现和解决这其中潜在的死锁风险,而不是等到最后真正出现死锁时给用户带来糟糕的体验。应运而生的就是 lockdep 死锁检测模块,在 2006 年已经引入内核(https://lwn.net/Articles/185666/)。

相关内核配置选项
    CONFIG_PROVE_LOCKING

    This feature enables the kernel to report locking related deadlocks before they actually occur. For more details, see Documentation/locking/lockdep-design.txt.

    CONFIG_DEBUG_LOCK_ALLOC

    Detect incorrect freeing of live locks.

    CONFIG_DEBUG_LOCKDEP

    The lock dependency engine will do additional runtime checks to debug itself, at the price of more runtime overhead.

    CONFIG_LOCK_STAT

    Lock usage statistics. For more details, see Documentation/locking/lockstat.txt

    CONFIG_DEBUG_LOCKING_API_SELFTESTS

    The kernel to run a short self-test during bootup in start_kernel(). The self-test checks whether common types of locking bugs are detected by debugging mechanisms or not. For more details, see lib/locking-selftest.c

死锁问题分析

    死锁就是多个进程(线程)因为等待别的进程已占有的自己所需要的资源而陷入阻塞的一种状态,
    死锁状态一旦形成,进程本身是解决不了的,需要外在的推动,才能解决,最重要的是死锁不仅仅影响进程业务,而且还会占用系统资源,影响其他进程。
    所以内核中设计了内核死锁检测机制,一旦发现死锁进程,就重启OS,快刀斩乱麻解决问题。
    之所以使用重启招数,还是在于分布式系统中可以容忍单点崩溃,不能容忍单点进程计算异常,否则进行死锁检测重启OS就得不偿失了。

 

什么是死锁

    互斥锁是保护临界资源被线程间(或进程间)互斥的访问临界资源,当一个线程得到锁不释放时另一个线程申请时必须等待。
    当多个线程因为竞争资源而造成的一种僵局(互相等待),如果不施以援手,这些进程将永远等待。
    简单的说:就是俩个或多个进程无止境的等候,永远不会成立的条件的一种系统状态。

 

死锁产生的原因

    ① 系统资源不足:系统中所拥有的资源其数量不足以满足线程运行的需要,使得在运行过程中,因争夺资源而陷入僵局。
    ② 线程间推进顺序非法:线程间在运行过程中,申请和释放的顺序不合法。
    ③ 资源分配不当。

 

死锁产生的四个条件

    互斥条件:在一段时间内,某资源只能被一个线程所占用,若此时其它线程申请则必须等待,直到当前线程用完释放。
    不可剥夺条件:线程在已获得的资源未使用完之前不能被抢占,只能自己使用完后自己释放
    请求和保持条件:线程已拥有一个资源,此时它又申请新的资源,若新资源被其它线程占用,当前线程则会阻塞等待,但其会保持不放自己获得的资源
    循环等待条件:在发生死锁时,必然存在一个循环链,即线程集合{T0,T1,T2,…,Tn}中T0正在等待T1占用的资源,T1正在等待T2占用的资源,………,Tn正在等待Tn占用的资源

 

死锁检测与恢复

    对于资源
        抢占资源恢复
        回滚到安全状态恢复
    对于进程
        杀死进程恢复

    
参考文章:
    https://blog.csdn.net/ccwzhu/article/details/81171092

一、D状态死锁检测

所谓D状态死锁:进程长时间(系统默认配置120秒)处于TASK_UNINTERRUPTIBLE 睡眠状态,这种状态下进程不响应异步信号。如:进程与外设硬件的交互(如read),通常使用这种状态来保证进程与设备的交互过程不被打断,否则设备可能处于不可控的状态。

内核D状态死锁检测就是hung_task机制,主要代码就在kernel/hung_task.c文件。

具体实现原理:

1.创建Normal级别的khungtaskd内核线程,在死循环中每隔sysctl_hung_task_timeout_secs时间后check一下,用schedule_timeout定时(节约定时器浪费的CPU)。

2.调用do_each_thread,while_each_thread宏遍历所有的进程信息,如果有D状态进程,则检查最近切换次数和task计算是否一致,即最近是否有调度切换,如果一致,则没有切换,打印相关信息,并根据sysctl_hung_task_panic开关决定是否重启。

对应用户态控制的proc接口有:
/proc/sys/kernel/hung_task_timeout_secs,hung_task_panic等。        
    
    
二、R状态死锁检测

所谓R状态死锁:进程长时间(系统默认配置60秒)处于TASK_RUNNING 状态垄断cpu而不发生切换,一般情况下是进程关抢占后长时候干活,有时候可能进程关抢占后处于死循环或者睡眠后,这样就造成系统异常。

补充:lockdep不是所谓的死锁。

内核R状态死锁检测机制就是lockdep机制,入口即是lockup_detector_init函数。

1.通过cpu_callback函数调用watchdog_enable,在每个CPU core上创建SCHED_FIFO级别的实时线程watchdog,其中使用了hrtimer定时器,控制检查周期。

2.hrtimer定时器调用watchdog_timer_fn进行清狗的时间检查,而线程则每次重置清狗时间,如果watchdog_timer_fn发现狗的重置时间已经和当前时间差出危险值,则根据开关进行panic处理。

对应用户态控制的proc接口有:

/proc/sys/kernel/watchdog_thresh,softlockup_panic等。

你可能感兴趣的:(日常,开发环境)