オペレーティングシステム-Linuxカーネルデッドロック検出(簡単なデッドロックデモを作成)

問題の説明

    実際の製品操作中に、Linuxシステムがフリーズし、画面に有効なシリアルポートの印刷情報がなく、ネットワークが中断され、キーボードとマウスが応答しません。
    この障害現象は、Linuxカーネルのデッドロックが原因である可能性があります。有効な印刷情報がなく、カーネルログにレコードがないため、障害の根本原因を特定することはできません。
    
 デッドロックが発生する前にLinuxカーネルに関連情報を出力させる方法は、問題の場所にとって特に重要です。効果的な方法の1つは、「カーネルハッキング」オプションをオンにしてから、カーネルを再コンパイルすることです。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デッドロックの形成を導入しました。トピックに戻り、カーネルに戻ります。複雑なロックが何千もあり、すべての開発者がspin_lock、spin_lock_irq、spin_lock_irqsave、spin_lock_nestedの違いに精通している必要はありません。したがって、ロックアップが発生する前に、実際の死亡が発生するまで待つのではなく、治療ではなく予防の良い仕事をし、問題が発生する前に予防し、事前に発見し、開発段階で潜在的なデッドロックリスクを発見して解決する必要があります。ロック時間はユーザーに悪い経験をもたらします。lockdepデッドロック検出モジュールが登場し、2006年にカーネルに導入されました(https://lwn.net/Articles/185666/)。

相关内核配置选项
    CONFIG_DEBUG_RT_MUTEXES=y
    检测rt mutex的死锁,并自动报告死锁现场信息。

    CONFIG_DEBUG_SPINLOCK=y
    检测spinlock的未初始化使用等问题。配合NMI watchdog使用,能发现spinlock死锁。

    CONFIG_DEBUG_MUTEXES=y
    检测并报告mutex错误

    CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
    检测wait/wound类型mutex的slowpath测试。
     
    CONFIG_DEBUG_LOCK_ALLOC=y
    检测使用中的锁(spinlock/rwlock/mutex/rwsem)被释放,或者使用中的锁被重新初始化,或者在进程退出时持有锁。
     
    CONFIG_PROVE_LOCKING=y
    使内核能在死锁发生前报告死锁详细信息。参见/proc/lockdep_chains。

    CONFIG_LOCKDEP=y
    整个Lockdep的总开关。参见/proc/lockdep、/proc/lockdep_stats。

    CONFIG_LOCK_STAT=y
    记锁持有竞争区域的信息,包括等待时间、持有时间等等信息。参见/proc/lock_stat。

    CONFIG_DEBUG_LOCKDEP=y
    会对Lockdep的使用过程中进行更多的自我检测,会增加很多额外开销。

    CONFIG_DEBUG_ATOMIC_SLEEP=y
    在atomic section中睡眠可能造成很多不可预测的问题,这些atomic section包括spinlock持锁、rcu读操作、禁止内核抢占部分、中断处理中等等。

デッドロック問題の分析

    デッドロックとは、他のプロセスが必要なリソースを占有するのを待っているために複数のプロセス(スレッド)がブロックされている
    状態ですデッドロック状態が形成されると、プロセス自体を解決できず、外部からの昇格が必要になります。解決すべき最も重要なことは、デッドロックがプロセスビジネスに影響を与えるだけでなく、システムリソースを消費し、他のプロセスにも影響を与えることです。
    そのため、カーネル内にカーネルデッドロック検出メカニズムが設計されており、デッドロックプロセスが検出されると、OSが再起動され、問題は迅速に解決されます。
    再起動戦術を使用する理由は、分散システムがシングルポイントクラッシュを許容でき、シングルポイントプロセス計算の異常を許容できないためです。そうでない場合、デッドロックの検出とOSの再起動は損失の価値がありません。

 

デッドロックとは

    相互除外ロックは、重要なリソースがスレッド間(またはプロセス間)で相互に排他的になるのを防ぎます。スレッドがロックを取得して解放しない場合、別のスレッドはそれを適用するときに待機する必要があります。
    複数のスレッドがリソースをめぐって競合し、デッドロック(互いに待機)を引き起こす場合、これらのプロセスは、役に立たない場合は永久に待機します。
    簡単に言えば、2つ以上のプロセスが際限なく待機し、条件が確立されないシステム状態です。

 

デッドロックの原因

    ①システムリソースが不足している:システム内のリソース数がスレッド操作のニーズを満たすのに十分でなく、操作中のリソース競合のためにデッドロックになっています。
    ②スレッド間の前進シーケンスが不正:スレッド間の適用と解放のシーケンスが不正です。
    ③不適切なリソース割り当て。

 

デッドロックの4つの条件

    相互に排他的な条件:一定期間内に、特定のリソースは1つのスレッドのみが占有できます。この時点で他のスレッドが適用される場合、現在のスレッドが使い果たされて解放されるまで待機する必要があります。
    不可侵の状態:リソースが使い果たされる前にスレッドをプリエンプトすることはできません。スレッドは、使用後にのみ
    要求と保留の状態を解放できます。スレッドはすでにリソースを所有しており、この時点で新しいリソースに適用されます。新しいリソースが他のスレッドが占有し、現在のスレッドはブロックして待機しますが、取得したリソースは保持します。
    ループ待機条件:デッドロックが発生すると、ループチェーン、つまりスレッドセット{T0、T1、T2、...、Tnが存在する必要があります。 } T0はT1が占有するリソースを待機しています、T1はT2が占有するリソースを待機しています...、TnはTnが占有するリソースを待機しています

 

よくある間違い

    AA:繰り返しロックする
    ABBA:一度ABシーケンスを使用してロックし、次にBAを使用して
    ABBCCAをロックする:このタイプはABBAの拡張です。ABオーダー、ABオーダー、CAオーダー。この種のロックは手動で見つけるのが困難です。
    複数回ロックを解除する

 

デッドロックの検出と回復

    リソース
        プリエンプションの場合、リソースリカバリ
        は安全な状態にロールバックし、
    プロセス
        キルのリカバリ、プロセスリカバリ

    
参考記事:
    https //blog.csdn.net/ccwzhu/article/details/81171092

1つ、D状態のデッドロック検出

いわゆるD状態デッドロック:プロセスは長時間(デフォルトでは120秒)TASK_UNINTERRUPTIBLEスリープ状態にあります。この状態では、プロセスは非同期信号に応答しません。例:プロセスと周辺ハードウェア間の相互作用(読み取りなど)。この状態は通常、プロセスとデバイス間の相互作用が中断されないようにするために使用されます。中断されない場合、デバイスは制御不能な状態になる可能性があります。

カーネルD状態のデッドロック検出はhung_taskメカニズムであり、メインコードはkernel /hung_task.cファイルにあります。

具体的な実現原則:

1.通常レベルのkhungtaskdカーネルスレッドを作成し、無限ループ内のすべてのsysctl_hung_task_timeout_secs時間をチェックし、schedule_timeoutを使用して時間を計ります(タイマーによって浪費されるCPUを節約します)。

2. do_each_thread、while_each_threadマクロを呼び出して、すべてのプロセス情報をトラバースします。D状態のプロセスがある場合は、最近の切り替えの数がタスク計算と一致しているかどうか、つまり、最近スケジューリング切り替えがあったかどうかを確認します。一貫している場合は、切り替えがありません。関連情報を印刷します。 sysctl_hung_task_panicスイッチは、再起動するかどうかを決定します。

ユーザーモード制御に対応するprocインターフェイスは、
/ proc / sys / kernel / hung_task_timeout_secs、hung_task_panicなどです。        
    
    
2、R状態のデッドロック検出

いわゆるR状態デッドロック:プロセスは、切り替えなしでcpuを独占するために、長時間(デフォルトでは60秒)TASK_RUNNING状態にあります。通常、プロセスは閉じられ、長時間プリエンプトされて動作します。プロセスが閉じられてプリエンプトされた後、無限ループになる場合があります。睡眠後、これはシステム異常を引き起こします。

追加:lockdepはいわゆるデッドロックではありません。

カーネルR状態のデッドロック検出メカニズムはlockdepメカニズムであり、エントリポイントはlockup_detector_init関数です。

1. cpu_callback関数を介してwatchdog_enableを呼び出し、各CPUコアにSCHED_FIFOレベルでリアルタイムスレッドウォッチドッグを作成します。これは、hrtimerタイマーを使用してチェックサイクルを制御します。

2. hrtimerタイマーがwatchdog_timer_fnを呼び出して犬の片付け時間を確認し、スレッドは毎回犬の片付け時間をリセットします。watchdog_timer_fnが犬のリセット時間が現在の時刻から危険にさらされていないことを検出すると、スイッチに従ってパニック処理が実行されます。

ユーザーモード制御に対応するprocインターフェイスは次のとおりです。

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

 

ロック情報を表示する

    /proc/sys/kernel/lock_stat  /* 置位则可以查看/proc/lock_stat统计信息,清除则关闭lockdep统计信息。 */
    /proc/lock_stat             /* 关于锁的使用统计信息 */
    /proc/lockdep               /* 存在依赖关系的锁 */
    /proc/lockdep_stats         /* 存在依赖关系锁的统计信息 */
    /proc/lockdep_chains        /* 依赖关系锁链表 */
    /proc/locks                 /*  */
    /proc/sys/kernel/prove_locking
    /proc/sys/kernel/max_lock_depth
    /sys/kernel/debug/tracing/events/lock  /* 内核提供了Tracepoint协助发现锁的使用问题 */

簡単なデッドロックデモを書く

#include <linux/module.h>
#include <linux/kernel.h>

static spinlock_t hack_spinA;
static spinlock_t hack_spinB;

void hack_spinAB(void)
{
  printk("hack_lockdep:A->B\n");
  spin_lock(&hack_spinA);
  spin_lock(&hack_spinB);
}

void hack_spinBA(void)
{
  printk("hack_lockdep:B->A\n");
  spin_lock(&hack_spinB);
  spin_lock(&hack_spinA);
}

static int __init lockdep_test_init(void)
{
  printk("al: lockdep error test init\n");
  spin_lock_init(&hack_spinA);
  spin_lock_init(&hack_spinB);

  hack_spinAB();
  hack_spinBA();
  return 0;
}

module_init(lockdep_test_init);

対応するMakefile

obj-m:=spin_lock_deadlock.o

#声明当前的架构,这里用户需要根据实际情况选择架构类型
export ARCH=arm
#声明交叉编译工具链,这里用户需要根据实际情况选择对应的工具链
export CROSS_COMPILE=arm-himix200-linux-

#源码目录变量,这里用户需要根据实际情况选择路径
#下面作者是将Linux的源码目录
KERDIR := /home1/zhugeyifan/source/K5/3519av100/packages/linux_lsp/kernel/linux-4.9.37/

#当前目录变量
CURDIR := $(shell pwd)

#make命名默认寻找第一个目标
#make -C就是指调用执行的路径
#$(KERDIR)Linux源码目录,作者这里指的是.../linux-4.9.37/
#$(CURDIR)当前目录变量
#modules要执行的操作
#注意!假如复制到Makefile后,下行提示红色,把 all:下面一行的make前面的空格删除后添加(Tab)制表符
all:
	make -C $(KERDIR) M=$(CURDIR) modules

#make clean执行的操作是删除后缀为o的文件
#注意!假如复制到Makefile后,下行提示红色,把 clean:下面一行的make前面的空格删除后添加(Tab)制表符
clean:
	make -C $(KERDIR) M=$(CURDIR) clean

エラーを報告する場合は、次の記事を読むことをお勧めします:オペレーティングシステム-Linuxは単純なkoモジュールをコンパイルします

 

生成

対応するkoモジュールを生成します。

spin_lock_deadlock.ko

 

実施した

上記のデッドロック検出モジュールが機能するかどうかをテストします

エラーを実行して報告します。これは、デッドロック検出モジュールが有効になったことを意味します。

watchdog: BUG: soft lockup - CPU#0 stuck for 22s! [insmod:306]
Modules linked in: spin_lock_deadlock(PO+) hi_user(O) hi_mipi_rx(O) hi3519av100_acodec(PO) hi3519av100_adec(PO) hi3519av100_aenc(PO) hi3519av100_ao(PO) hi3519av100_ai(PO) hi3519av100_aio(PO) hi3519av100_hdmi(PO) hi_sensor_spi(O) hi_sensor_i2c(O) hi_piris(O) hi_pwm(O) hi3519av100_dpu_match(PO) hi3519av100_dpu_rect(PO) hi3519av100_dsp(PO) hi3519av100_nnie(PO) hi_ipcm(O) hi3519av100_ive(PO) hi3519av100_vdec(PO) hi3519av100_vfmw(PO) hi3519av100_jpegd(PO) hi3519av100_jpege(PO) hi3519av100_h265e(PO) hi3519av100_h264e(PO) hi3519av100_vedu(PO) hi3519av100_chnl(PO) hi3519av100_venc(PO) hi3519av100_rc(PO) hifb(O) hi3519av100_vo(PO) hi3519av100_avs(PO) hi3519av100_vpss(PO) hi3519av100_vi(PO) hi3519av100_isp(PO) hi3519av100_dis(PO) hi3519av100_vgs(PO) hi3519av100_gdc(PO) hi3519av100_rgn(PO) hi3519av100_tde(PO) hi3519av100_sys(PO) hi3519av100_base(PO) hi_osal(O) sys_config(O)
CPU: 0 PID: 306 Comm: insmod Tainted: P           O    4.9.37 #402
Hardware name: Generic DT based system
task: d47ace40 task.stack: d470c000
PC is at _raw_spin_lock+0x2c/0x40
LR is at hack_spinBA+0x20/0x2c [spin_lock_deadlock]
pc : [<c06a4a04>]    lr : [<bf00204c>]    psr: 80000013
sp : d470ddf8  ip : 00000000  fp : 00000024
r10: 00000000  r9 : bf002100  r8 : 00000000
r7 : d47a3000  r6 : d47a3ec0  r5 : ffffe000  r4 : bf002300
r3 : 00000000  r2 : 00000001  r1 : 00000000  r0 : bf002304
Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5383d  Table: 238dc06a  DAC: 00000051
CPU: 0 PID: 306 Comm: insmod Tainted: P           O    4.9.37 #402
Hardware name: Generic DT based system
[<c010f8b8>] (unwind_backtrace) from [<c010b4e0>] (show_stack+0x10/0x14)
[<c010b4e0>] (show_stack) from [<c0380bbc>] (dump_stack+0x84/0x98)
[<c0380bbc>] (dump_stack) from [<c0194ff8>] (watchdog_timer_fn+0x248/0x2c0)
[<c0194ff8>] (watchdog_timer_fn) from [<c016f534>] (__hrtimer_run_queues+0x128/0x1bc)
[<c016f534>] (__hrtimer_run_queues) from [<c016f71c>] (hrtimer_interrupt+0xa8/0x204)
[<c016f71c>] (hrtimer_interrupt) from [<c054b594>] (arch_timer_handler_phys+0x28/0x30)
[<c054b594>] (arch_timer_handler_phys) from [<c0163688>] (handle_percpu_devid_irq+0x74/0x134)
[<c0163688>] (handle_percpu_devid_irq) from [<c015eb30>] (generic_handle_irq+0x24/0x34)
[<c015eb30>] (generic_handle_irq) from [<c015f054>] (__handle_domain_irq+0x5c/0xb4)
[<c015f054>] (__handle_domain_irq) from [<c0101438>] (gic_handle_irq+0x48/0x8c)
[<c0101438>] (gic_handle_irq) from [<c010bfcc>] (__irq_svc+0x6c/0x90)
Exception stack(0xd470dda8 to 0xd470ddf0)
dda0:                   bf002304 00000000 00000001 00000000 bf002300 ffffe000
ddc0: d47a3ec0 d47a3000 00000000 bf002100 00000000 00000024 00000000 d470ddf8
dde0: bf00204c c06a4a04 80000013 ffffffff
[<c010bfcc>] (__irq_svc) from [<c06a4a04>] (_raw_spin_lock+0x2c/0x40)
[<c06a4a04>] (_raw_spin_lock) from [<bf00204c>] (hack_spinBA+0x20/0x2c [spin_lock_deadlock])
[<bf00204c>] (hack_spinBA [spin_lock_deadlock]) from [<d47a3ec0>] (0xd47a3ec0)
INFO: rcu_sched self-detected stall on CPU
	0-...: (59672 ticks this GP) idle=4c3/140000000000001/0 softirq=34656/34656 fqs=14660 
	 (t=60000 jiffies g=2778 c=2777 q=166)
Task dump for CPU 0:
insmod          R  running task        0   306    218 0x00000003
[<c010f8b8>] (unwind_backtrace) from [<c010b4e0>] (show_stack+0x10/0x14)
[<c010b4e0>] (show_stack) from [<c019857c>] (rcu_dump_cpu_stacks+0xa8/0xc4)
[<c019857c>] (rcu_dump_cpu_stacks) from [<c016bf54>] (rcu_check_callbacks+0x714/0x890)
[<c016bf54>] (rcu_check_callbacks) from [<c016e780>] (update_process_times+0x34/0x5c)
[<c016e780>] (update_process_times) from [<c017eaf0>] (tick_sched_timer+0x68/0x268)
[<c017eaf0>] (tick_sched_timer) from [<c016f534>] (__hrtimer_run_queues+0x128/0x1bc)
[<c016f534>] (__hrtimer_run_queues) from [<c016f71c>] (hrtimer_interrupt+0xa8/0x204)
[<c016f71c>] (hrtimer_interrupt) from [<c054b594>] (arch_timer_handler_phys+0x28/0x30)
[<c054b594>] (arch_timer_handler_phys) from [<c0163688>] (handle_percpu_devid_irq+0x74/0x134)
[<c0163688>] (handle_percpu_devid_irq) from [<c015eb30>] (generic_handle_irq+0x24/0x34)
[<c015eb30>] (generic_handle_irq) from [<c015f054>] (__handle_domain_irq+0x5c/0xb4)
[<c015f054>] (__handle_domain_irq) from [<c0101438>] (gic_handle_irq+0x48/0x8c)
[<c0101438>] (gic_handle_irq) from [<c010bfcc>] (__irq_svc+0x6c/0x90)
Exception stack(0xd470dda8 to 0xd470ddf0)
dda0:                   bf002304 00000000 00000001 00000000 bf002300 ffffe000
ddc0: d47a3ec0 d47a3000 00000000 bf002100 00000000 00000024 00000000 d470ddf8
dde0: bf00204c c06a4a04 80000013 ffffffff
[<c010bfcc>] (__irq_svc) from [<c06a4a04>] (_raw_spin_lock+0x2c/0x40)
[<c06a4a04>] (_raw_spin_lock) from [<bf00204c>] (hack_spinBA+0x20/0x2c [spin_lock_deadlock])
[<bf00204c>] (hack_spinBA [spin_lock_deadlock]) from [<d47a3ec0>] (0xd47a3ec0)

 


参考記事

    https://blog.csdn.net/ccwzhu/article/details/81171092デッドロック
    の形成
   Linuxデッドロック検出モジュールLockdepはじめに  

 

おすすめ

転載: blog.csdn.net/Ivan804638781/article/details/100740857