windbg调试驱动自旋锁死锁

本文介绍如何使用Windbg的!running扩展来诊断Windows内核驱动程序中的死锁问题。通过模拟两个线程以不同顺序获取自旋锁导致的死锁场景,并展示了如何利用Windbg定位发生死锁的具体位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    驱动程序中常用自旋锁来保护互斥资源。由于驱动常以异步的方式工作,很难保证不发生死锁,这就需要一种调试命令来查看引发死锁的位置。windbg的!running扩展提供了这样的能力。看看windbg help的说明:

!running
显示正在处理器上运行的线程
The !running extension displays a list of running threads on all processors of the target computer.
    这段描述正适合检索自旋锁这种尸位素餐的行为。

    下面的代码片模拟了这样的场景:2个线程已不同的顺序获得自旋锁并且不释放锁,这样必然引起死锁:

#include <wdm.h>

typedef struct _THREADCTX
{
	KSPIN_LOCK spin1, spin2;
}THREADCTX;

VOID KeThreadRoutine1(void* StartContext)
{
	unsigned int i=0;
	THREADCTX* threadCtx = (THREADCTX*)StartContext;
	KIRQL oldIrql1, oldIrql2;
	KEVENT waitEvt;
	LARGE_INTEGER tickIntval;

	tickIntval = RtlConvertLongToLargeInteger(-10 * 1000 * 1000 * 1);
	KeInitializeEvent(&waitEvt, SynchronizationEvent, FALSE);
	KeAcquireSpinLock(&threadCtx->spin2, &oldIrql2); //先获得2号锁 再获得1号;另一个线程已相反的顺序获得锁
	KeAcquireSpinLock(&threadCtx->spin1, &oldIrql1);

	while (1)
	{
		KeDelayExecutionThread(KernelMode,FALSE,&tickIntval);
		i++;
	}

	return;
}

VOID KeThreadRoutine2(void* StartContext)
{
	unsigned int i=0;
	THREADCTX* threadCtx = (THREADCTX*)StartContext;
	KIRQL oldIrql1, oldIrql2;
	KEVENT waitEvt;
	LARGE_INTEGER tickIntval;

	tickIntval = RtlConvertLongToLargeInteger(-10 * 1000 * 1000 * 1);
	KeInitializeEvent(&waitEvt, SynchronizationEvent, FALSE);
	KeAcquireSpinLock(&threadCtx->spin1, &oldIrql1);
	KeAcquireSpinLock(&threadCtx->spin2, &oldIrql2);

	while (1)
	{
		KeDelayExecutionThread(KernelMode,FALSE,&tickIntval);
		i++;
	}

	return;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT drvObj, PUNICODE_STRING regPath)
{
	NTSTATUS status;
	HANDLE threadHd;
	THREADCTX threadCtx;
	CLIENT_ID cid;
	KIRQL oldIrql1, oldIrql2;
	LARGE_INTEGER tickIntval;
	KEVENT waitEvt;
	
	
	
	tickIntval = RtlConvertLongToLargeInteger(-10*1000*1000*1);
	KeInitializeEvent(&waitEvt, SynchronizationEvent, FALSE);
	RtlZeroMemory(&threadCtx, sizeof(THREADCTX));
	KeInitializeSpinLock(&threadCtx.spin1);
	KeInitializeSpinLock(&threadCtx.spin2);
        //创建线程
	status = PsCreateSystemThread(&threadHd, 0, NULL, NULL, &cid, KeThreadRoutine1, &threadCtx);
	status = PsCreateSystemThread(&threadHd, 0, NULL, NULL, &cid, KeThreadRoutine2, &threadCtx);
	if (status != STATUS_SUCCESS)
		return STATUS_FAILED_DRIVER_ENTRY;

	return STATUS_SUCCESS;
}
    加载驱动后,系统可能马上卡死。用windbg查看一下发生了什么:

kd> !running -it

System Processors: (00000001) 
  Idle Processors: (00000000) 
;Current给出了占用CPU运行的thread
       Prcbs     Current   Next    
  0    82b30d20  8689d020            ................

ChildEBP RetAddr  
8cae9c08 82a7238f nt!RtlpBreakWithStatusInstruction
8cae9c10 82a72361 nt!KdCheckForDebugBreak+0x22
8cae9c40 82a721ef nt!KeUpdateRunTime+0x164
8cae9c98 82a77577 nt!KeUpdateSystemTime+0x613
8cae9c98 82e1a749 nt!KeUpdateSystemTimeAssist+0x13
8cae9d1c 9447b04f hal!KeAcquireSpinLockRaiseToSynch+0x39
8cae9d50 82c1566d deadlock!KeThreadRoutine1+0x3f [c:\studio\deadlock\deadlock.c @ 18]  <---发生死锁的模块
8cae9d90 82ac70d9 nt!PspSystemThreadStartup+0x9e
00000000 00000000 nt!KiThreadStartup+0x19
    !running输出了发生死锁的线程TEB和线程堆栈,可以用!thread查看更详细的信息:

kd> !thread 8689d020            
THREAD 8689d020  Cid 0004.0dc0  Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 0
Not impersonating
DeviceMap                 89e088d8
Owning Process            84fe8ae8       Image:         System
Attached Process          N/A            Image:         N/A
Wait Start TickCount      8955           Ticks: 1292 (0:00:00:20.155)
Context Switch Count      1             
UserTime                  00:00:00.000
KernelTime                00:00:20.155
Win32 Start Address deadlock!KeThreadRoutine1 (0x9447b010)
Stack Init 8cae9fd0 Current 8cae9d84 Base 8caea000 Limit 8cae7000 Call 0
Priority 8 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr  Args to Child              
8cae9c08 82a7238f 00000001 82a72361 00000000 nt!RtlpBreakWithStatusInstruction (FPO: [1,0,0])
8cae9c10 82a72361 00000000 00000000 00009d88 nt!KdCheckForDebugBreak+0x22 (FPO: [0,0,0])
8cae9c40 82a721ef 82e1a749 a7ff8a01 00000000 nt!KeUpdateRunTime+0x164
8cae9c98 82a77577 95bc5002 95bc5002 000000d1 nt!KeUpdateSystemTime+0x613
8cae9c98 82e1a749 95bc5002 95bc5002 000000d1 nt!KeUpdateSystemTimeAssist+0x13 (FPO: [0,2] TrapFrame @ 8cae9cac)
8cae9d1c 9447b04f 00000dbc ff676980 ffffffff hal!KeAcquireSpinLockRaiseToSynch+0x39 (FPO: [0,0,0])
8cae9d50 82c1566d 807f1ad0 d1cb3c49 00000000 deadlock!KeThreadRoutine1+0x3f (FPO: [Non-Fpo]) (CONV: stdcall) [c:\studio\deadlock\deadlock.c @ 18]
8cae9d90 82ac70d9 9447b010 807f1ad0 00000000 nt!PspSystemThreadStartup+0x9e
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x19

后记:

    这个命令的确很简单,也挺好用,但是为了找到这样的命令前后花了我近一周的时间,所以记录在这,也希望能帮到有同样需求的人~

### 回答1: 使用 Windbg 分析互锁可以使用一些特定的命令来查看和分析系统中的互锁信息。其中一些常用的命令如下: 1. !locks - 显示系统中所有的互斥体和自旋锁的信息。 2. !handle - 查看特定进程中的所有句柄。 3. !critsec - 查看特定互斥体的详细信息。 4. !dlk - 查看死锁信息。 5. k - 查看堆栈信息,可以查看调用 WaitForSingleObject() 和 WaitForMultipleObjects() 函数的线程。 还有一些第三方工具如: 1. SpinlockTracer 2. DeadlockDetective 建议配合参考资料学习使用,并在正式的debug场景中使用。 ### 回答2: 在Windbg中分析互锁步骤如下: 1. 打开目标进程:在Windbg中选择“文件”>“打开进程”或使用命令行打开目标进程。 2. 设置符号路径:在Windbg中选择“文件”>“符号文件路径”,将包含目标二进制文件和符号文件的目录添加到符号路径中。 3. 启用调试选项:在Windbg中选择“调试”>“调试选项”,启用调试器的相关选项,例如“调试输出”、“源代码路径”等。 4. 附加到目标进程:在Windbg中选择“调试”>“附加到进程”,选择目标进程并附加。 5. 设置断点:在Windbg中选择“调试”>“新建断点”,并根据需要设置互锁相关的断点。例如,可以设置在进入或离开互锁对象的互斥体函数处设置断点。 6. 运行目标进程:在Windbg中选择“调试”>“运行”,让目标进程运行到断点处。 7. 分析互锁:一旦目标进程到达断点,可以使用Windbg调试命令和命令扩展来分析互锁。可以使用命令如“!locks”查看进程中的所有互锁对象,“!analyze -v”分析调用栈以查找互锁问题的根本原因等。 8. 调试完成:当分析完成后,可以选择继续运行目标进程或结束调试。 以上是使用Windbg分析互锁的基本步骤。在实际应用中,还可以根据具体情况使用其他Windbg调试命令和技术来深入分析和解决互锁问题。 ### 回答3: Windbg是一款Windows操作系统上的调试工具,可以用来分析和调试应用程序的问题。在分析互锁时,以下是一些基本步骤: 1. 打开Windbg工具,并使用`file`命令加载需要分析的应用程序的可执行文件。 2. 使用`g`命令运行应用程序,使其在Windbg中开始运行。 3. 在应用程序出现问题的地方设置断点,可以使用`bp`命令设置断点,然后运行应用程序。 4. 当应用程序运行到断点处时,可以使用`!locks`命令来查看当前进程中的所有互锁对象。该命令将显示互锁对象的名称、所属线程、当前状态以及等待该互锁对象的线程列表等信息。 5. 使用`kb`命令查看当前线程的堆栈跟踪,确定哪些线程正在等待互锁对象,以及导致互锁的原因。 6. 使用`~~[数字]s`命令查看指定线程的堆栈跟踪,可以从中找出可能的问题代码。 7. 使用`!analyse -v`命令对问题进行进一步分析,该命令可以提供更多的调试信息,帮助定位问题。 8. 根据分析结果,可以在代码中找出可能存在的资源竞争或死锁问题。根据具体情况,可以修改代码、添加同步机制或者调整互锁的顺序等,以解决互锁问题。 以上是使用Windbg分析互锁的基本步骤,使用这些命令和功能可以快速定位和解决互锁问题,提高应用程序的稳定性和性能。在实际使用中,可以根据具体情况使用其他命令和调试技巧来深入分析问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值