《Windows via C/C++》学习笔记 —— “线程同步”之“检测死锁”

  本来这篇内容在书中是在“其他线程同步函数”这一节中的。这节中介绍了另外的几个等待函数,比如WaitForInputIdle、MsgWaitForMultipleObjects、WaitForDebugEvent,感觉用途不大,只有SignalObjectAndWait这个函数用途比较大,该函数笔者已经在前面的“等待函数”这一篇中写过了。

  本篇主要讨论这本书新增加的内容,就是Windows Vista中提供的调查死锁的方法。

 

  在Windows Vista中,提供了用来“检测线程死锁”的一组API,Jeffrey Richter先生在书中将其称为“Wait Chain Traversal”(WCT)API。查看了网上的翻译,叫做“等待链遍历”API函数,我也不知道这么翻译是否正确或权威,就姑且这么叫着吧,下面简称为WCT。

  WCT函数允许你列出“锁”,并检测死锁,无论是单个进程的,还是跨进程的。Windows对以下线程同步机制的锁保持着跟踪:

可能的锁

描述

关键代码段

Windows对关键代码段属于哪个线程保持着跟踪

互斥内核对象

Windows对互斥内核对象属于哪个线程保持着跟踪,即使互斥内核对象被丢弃

进程和线程

Windows对一个线程正在等待哪个进程或线程保持着跟踪,另外跟踪进程和线程是否终止

SendMessage

Windows保持跟踪正在等待SendMessage函数返回的线程

COM初始化和调用

调用CoCreateInstance函数或COM对象的方法的线程被跟踪

高级本地过程调用(ALPC)

在Windows Vista中,ALPC代替了LPC,新的没有文档的内核进程内部的通信(IPC)机制

 

  需要注意的是,读写锁(SRWLock)同步机制没有被WPC所跟踪,另外某些内核对象,比如事件内核对象、信号量、等待定时器没有被跟踪。

 

  本书举了一个例子来演示了WPC函数的使用。本人总结了下,主要通过以下几个步骤来实现死锁的检测:

1、打开等待链:使用OpenThreadWaitChainSession函数

2、如果需要检测COM的话:使用RegisterWaitChainCOMCallback函数

3、取得等待链信息:使用GetThreadWaitChain函数

4、关闭等待链:使用CloseThreadWaitChainSession函数

 

  首先,介绍一下OpenThreadWaitChainSession函数:

HWCT OpenThreadWaitChainSession(
   DWORD dwFlags,       
// 0表示同步,WCT_ASYNC_OPEN_FLAG表示异步
   PWAITCHAINCALLBACK callback);    // 异步的话要设置该回调函数指针

 

  该函数成功,返回一个HWCT类型的句柄,该句柄要传递给CloseThreadWaitChainSession函数,在最后释放。如果失败,该函数返回NULL。

  一般地,您总是希望从中调用RegisterWaitChainCOMCallback函数以注册 CoGetCallState和CoGetActivationState这两个COM函数,以便WCT能够报告 COM所有权信息,由于并未记录这些函数,您需要从ole32.dll通过GetProcAddress 获得这些函数。本人认为,在您对WCT API进行初始化以查找COM死锁时,WCT API 应自动处理这种细小问题。

 

VOID WINAPI RegisterWaitChainCOMCallback(
   __in  PCOGETCALLSTATE CallStateCallback,   
// CoGetCallState函数地址
   __in  PCOGETACTIVATIONSTATE 
             ActivationStateCallback    
// CoGetActivationState函数地址
);

 

  你可以使用如下代码这样使用RegisterWaitChainCOMCallback函数:

 

PCOGETCALLSTATE CallStateCallback;       // 函数指针
PCOGETACTIVATIONSTATE ActivationStateCallback;      // 函数指针

HMODULE hOLE32DLL 
=  LoadLibrary(TEXT( " OLE32.DLL " )); // 加载OLE32.DLL

// 从ole32.dll中取得函数地址
CallStateCallback  =  (PCOGETCALLSTATE)
                     GetProcAddress(_hOLE32DLL, 
" CoGetCallState " );
ActivationStateCallback 
=  (PCOGETACTIVATIONSTATE)
             GetProcAddress(_hOLE32DLL, 
" CoGetActivationState " ); 

// 在等待链中注册COM函数
RegisterWaitChainCOMCallback(CallStateCallback,ActivationStateCallback);

 

  下面,第3步,使用GetThreadWaitChain函数取得等待链信息:

BOOL WINAPI GetThreadWaitChain(
   HWCT hWctSession,          
// 等待链句柄
   DWORD_PTR pContext,      // 异步方法中回调函数的参数
   DWORD dwFlags,               // 查询类型(位或组合)
   DWORD TID,                     // 要查询的线程的线程ID
   PDWORD pNodeCount,       // 返回值,指明等待链中的结点个数
   PWAITCHAIN_NODE_INFO pNodeInfoArray,      // 返回值,等待链结点信息
   LPBOOL pbIsCycle              // 返回值,TURE表示死锁
);

 

  pNodeCount参数如果返回的DWORD数据值为0,表明一个问题产生了,比如拒绝访问。这个时候可以调用GetLastError取得错误信息。

  这个函数中,pNodeInfoArray函数是一个WAITCHAIN_NODE_INFO结构的指针,该结构如下:

 

typedef  struct  _WAITCHAIN_NODE_INFO
{
    WCT_OBJECT_TYPE ObjectType;          
// 结点对象类型
    WCT_OBJECT_STATUS ObjectStatus;    // 结点对象状态

    union {
        
struct  {
            WCHAR ObjectName[WCT_OBJNAME_LENGTH];
            LARGE_INTEGER Timeout;    
//  Not implemented in v1
            BOOL Alertable;            //  Not implemented in v1
        } LockObject;

       
struct  {
           DWORD ProcessId;
           DWORD ThreadId;
           DWORD WaitTime;
           DWORD ContextSwitches;
       } ThreadObject;
    };
} WAITCHAIN_NODE_INFO, 
* PWAITCHAIN_NODE_INFO;

 

  该结构中的ObjectTyped成员,指明了当前等待链中的结点类型,ObjectStatus指明了当前该结点对象的状态,两者具体取值就看MSDN或本书吧。如果这个结点类型是一个“阻塞”状态的线程,即ObjectTyped为WctThreadType的话,内部的ThreadObject成员即有效。

 

  最后,不要忘记调用CloseThreadWaitChainSession关闭WCT句柄。当然,如果用LoadLibrary函数加载了OLE23.dll文件的话,也要记得FreeLibrary函数来释放。

 

  可以使用如下代码处理等待链中的结点:

 

HWCT hWCTSession  =  OpenThreadWaitChainSession( 0 , NULL);
if  (hWCTSession  ==  NULL)
     
return   0 ;

// 两个COM的函数指针
PCOGETCALLSTATE CallStateCallback;
PCOGETACTIVATIONSTATE ActivationStateCallback;

HMODULE hOLE32DLL 
=  LoadLibrary(TEXT( " OLE32.DLL " ));    // 加载OLE32.DLL
if  (hOLE32DLL  ==  NULL)
     
return   0 ;

// 从ole32.dll中取得两个函数的地址
CallStateCallback  =  (PCOGETCALLSTATE)
                     GetProcAddress(_hOLE32DLL, 
" CoGetCallState " );
ActivationStateCallback 
=  (PCOGETACTIVATIONSTATE)
                     GetProcAddress(_hOLE32DLL, 
" CoGetActivationState " );

// 注册COM函数,使得WCT可以报告COM相关事件
RegisterWaitChainCOMCallback(CallStateCallback,ActivationStateCallback);

// 在你需要处理的线程中:
DWORD dwNodesInChain  =   0 ;      // 链中的结点个数
BOOL bDeadlock  =  FALSE;        // 线程是否存在死锁
WAITCHAIN_NODE_INFO chain[WCT_MAX_NODE_COUNT]; // WCT节点结构数组

// 取得等待链信息,并判断是否正确,TID为线程ID
if  ( ! GetThreadWaitChain(hWCTSession, NULL, WCTP_GETINFO_ALL_FLAGS,
         TID, 
& dwNodesInChain, chain,  & bDeadlock))
{
     
// 函数调用失败,在此处理
      
//
      return   0 ;
}

//  开始处理等待链
dwNodesInChain  =  min(dwNodesInChain, WCT_MAX_NODE_COUNT);

if  (dwNodesInChain  ==   0 )      // 结点个数为0,可以调用GetLastError处理
      return   0 ;

// 可以在此处理本线程有关的等待链信息
if  (bDeadlock)
     
// 表明线程ID为TID的线程存在死锁

// 处理每个链中的节点
for  (DWORD current  =   0 ; current  <  dwNodesInChain;  ++ current)
{
     
// 在此处理练中每个结点信息
      
//
}

FreeLibrary(hOLE32DLL);    
// 释放COM模块
CloseThreadWaitChainSession(hWCTSession);     // 关闭WCT

 

转载于:https://www.cnblogs.com/wz19860913/archive/2008/08/15/1268901.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值