天天看點

windbg調試驅動自旋鎖死鎖

    驅動程式中常用自旋鎖來保護互斥資源。由于驅動常以異步的方式工作,很難保證不發生死鎖,這就需要一種調試指令來檢視引發死鎖的位置。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      

繼續閱讀