• Monitor.Wait初探(3)


    Waits until one or all of the specified objects are in the signaled state, an I/O completion routine or asynchronous procedure call (APC) is queued to the thread, or the time-out interval elapses.

    Syntax
    DWORD WINAPI WaitForMultipleObjectsEx(
      __in  DWORD nCount,
      __in  const HANDLE *lpHandles,
      __in  BOOL bWaitAll,
      __in  DWORD dwMilliseconds,
      __in  BOOL bAlertable
    );
     
    现在我们终于知道是WaitForMultipleObjectsEx的调用才导致了线程处于等待状态,因为Wait函数等待的锁对象一直没有得到signaled,原因是啥,因为没有Pulse嘛,关于Pulse如何工作的,这一点以后有时间还会讨论。但是WaitForMultipleObjectsEx等就等了,不是还有一个参数dwMilliseconds是控制超时的嘛,如果超时了就应该可以退出等待状态对不,那究竟本文示例中的等待会因为超时自动结束嘛?
    其实Reflector一下看看Monitor.Wait发现它调用了其重载:
    [SecuritySafeCritical]
    public static bool Wait(object obj)
    {
        return Wait(obj, -1, false);
    }
    
    
    
    

    而这个重载的第二个参数就是一个时间变量,传来-1应该就是无限等待的意思?我只是大胆猜测咯。至于最终调用WaitForMultipleObjectsEx的参dwMilliseconds数是多少,最好还是科学态度严谨一些。所以,

    我们还需要调试一番,先来看看dwMilliseconds的用法:

    dwMilliseconds[in]

    The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the specified objects are signaled, an I/O completion routine or APC is queued, or the interval elapses. If dwMilliseconds is zero, the function does not enter a wait state if the criteria is not met; it always returns immediately. If dwMilliseconds is INFINITE, the function will return only when the specified objects are signaled or an I/O completion routine or APC is queued.

    顺便再来看看另外一个参数bAlertable以及其他几个参数的用法(当然,这里略过不分析,我只需要确定dwMilliseconds的值是不是INFINITE就ok了,INFINITE=FFFFFFFF=-1):

    bAlertable [in]If this parameter is TRUEand the thread is in the waiting state, the function returns when the system queues an I/O completion routine or APC, and the thread runs the routine or function. Otherwise, the function does not return and the completion routine or APC function is not executed.

    A completion routine is queued when the ReadFileEx or WriteFileEx function in which it was specified has completed. The wait function returns and the completion routine is called only if bAlertable is TRUE and the calling thread is the thread that initiated the read or write operation. An APC is queued when you call QueueUserAPC.

    nCount [in]The number of object handles to wait for in the array pointed to by lpHandles. The maximum number of object handles is MAXIMUM_WAIT_OBJECTS. This parameter cannot be zero.

    lpHandles[in]An array of object handles. For a list of the object types whose handles can be specified, see the following Remarks section. The array can contain handles of objects of different types. It may not contain multiple copies of the same handle.

    If one of these handles is closed while the wait is still pending, the function's behavior is undefined.

    The handles must have the SYNCHRONIZE access right. For more information, see Standard Access Rights.

    bWaitAll[in]

    If this parameter is TRUE, the function returns when the state of all objects in the lpHandles array is set to signaled. If FALSE, the function returns when the state of any one of the objects is set to signaled. In the latter case, the return value indicates the object whose state caused the function to return.

    现在让我们再回到windbg,通过Debug菜单的重启命令重启被调试的程序,然后在调用WaitForMultipleObjectsEx的地方加一个断点,命令:

    bp KERNEL32!WaitForMultipleObjectsEx

    g命令 继续执行到断点停下,

    自动获得输出如下:

    0:000> bp KERNEL32!WaitForMultipleObjectsEx
    0:000> g
    ModLoad: 77da0000 77e49000   C:\WINDOWS\system32\ADVAPI32.dll
    ModLoad: 77e50000 77ee3000   C:\WINDOWS\system32\RPCRT4.dll
    ModLoad: 77fc0000 77fd1000   C:\WINDOWS\system32\Secur32.dll
    ModLoad: 603b0000 60416000   c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll
    ModLoad: 77f40000 77fb6000   C:\WINDOWS\system32\SHLWAPI.dll
    ModLoad: 77ef0000 77f39000   C:\WINDOWS\system32\GDI32.dll
    ModLoad: 77d10000 77da0000   C:\WINDOWS\system32\USER32.dll
    ModLoad: 77be0000 77c38000   C:\WINDOWS\system32\msvcrt.dll
    ModLoad: 76300000 7631d000   C:\WINDOWS\system32\IMM32.DLL
    ModLoad: 62c20000 62c29000   C:\WINDOWS\system32\LPK.DLL
    ModLoad: 73fa0000 7400b000   C:\WINDOWS\system32\USP10.dll
    ModLoad: 79e70000 7a419000   c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
    ModLoad: 78130000 781cb000   C:\WINDOWS\WinSxS\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.4053_x-ww_e6967989\MSVCR80.dll
    Breakpoint 0 hit
    eax=00b1ff1c ebx=00000000 ecx=00000003 edx=0000000d esi=00162138 edi=00000200
    eip=7c8095d8 esp=00b1fedc ebp=00b1fef4 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    KERNEL32!WaitForMultipleObjectsEx:
    7c8095d8 6a64            push    64h

    在kb一下获取当前的call stack:

    0:001> kb
    ChildEBP RetAddr  Args to Child             
    00b1fed8 7c80a115 00000003 00b1ff1c 00000000 KERNEL32!WaitForMultipleObjectsEx
    00b1fef4 79f339e1 00000003 00b1ff1c 00000000 KERNEL32!WaitForMultipleObjects+0x18
    00b1ff54 79f3393e 34fd7cea 00000000 79f327d4 mscorwks!DebuggerRCThread::MainLoop+0xe9
    00b1ff84 79f33865 34fd7cda 79f1c171 79f327d4 mscorwks!DebuggerRCThread::ThreadProc+0xe5
    00b1ffb4 7c80b729 00000000 79f1c171 79f327d4 mscorwks!DebuggerRCThread::ThreadProcStatic+0x9c
    00b1ffec 00000000 79f3381f 00000000 00000000 KERNEL32!BaseThreadStart+0x37

    我们看到stack最后停在KERNEL32!WaitForMultipleObjectsEx,说明我家的断点生效了,这不是废话嘛。注意看Args  to Child三列列出来的是函数的前三个参数,对照WaitForMultipleObjectsEx的原型,第一个参数nCount=3,第二个放的是对象句柄数组指针地址,第三个值0意味bWaitAll为False。那么问题又来了,我有五个参数啊,后面两个参数如何获取?别急慢慢来,我们看到kb的输出第一列ChildEBP,这个用途很大,Args to Child显示的三列就分别是EBP+4,EBP+C,EBP+10的值,而同理类推递增4,则第4,5个参数的地址是EBP+14,EBP+18.

    好了,由此我们再敲打命令dd 00b1fed8+14,输出如下:

    0:001> dd 00b1fed8+14
    00b1feec  ffffffff 00000000 00b1ff54 79f339e1
    00b1fefc  00000003 00b1ff1c 00000000 ffffffff
    00b1ff0c  34fd7c3a 00162138 80000200 00000000
    00b1ff1c  00000780 00000778 00000784 34fd7c22
    00b1ff2c  00162138 00162070 00162070 00000000
    00b1ff3c  00000001 00000003 ffffffff 00b1ff78
    00b1ff4c  7a3885ac 00000002 00b1ff84 79f3393e
    00b1ff5c  34fd7cea 00000000 79f327d4 00000000

    红色字体即是第四个参数的物理地址,而蓝色部分是其对应的值,ffffffff ,终于,在这里验证了自己的猜想。

    不过好奇心驱使使得我想知道最终WaitForMultipleObjectsEx等待的对象句柄究竟是个什麽玩意,该函数等待的是对象句柄的数组,所以其参数值对应数组第一个对象句柄的地址00b1ff1c ,使用dd 00b1ff1c L1获取数组的第一个对象句柄,输出如下:

    0:001> dd 00b1ff1c L1
    00b1ff1c  00000780

    使用命令: !handle 00000780 f获得句柄的具体信息:

    0:001> !handle 00000780 f
    Handle 780
      Type             Event
      Attributes       0
      GrantedAccess    0x1f0003:
             Delete,ReadControl,WriteDac,WriteOwner,Synch
             QueryState,ModifyState
      HandleCount      2
      PointerCount     3
      Name             <none>
      Object Specific Information
        Event Type Auto Reset
        Event is Waiting

    啊哈,原来是一个Event类型的句柄,且Event Type Auto Reset,且Event is Waiting,我很快想到了Windows线程同步的事件,也想到.Net中的AutoResetEvent和ManualResetEvent,但是这已经偏离了本文Monitor.Wait的调查路线,故就那样吧。

  • 相关阅读:
    Android 列表之分组ListView
    【转】容易忽视但是功能灰常强大的Java API
    onSaveInstanceState和onRestoreInstanceState的用处
    asp.net中url重写
    泛型数据类型转换
    关于 url重写后性能问题
    用CSS实现新闻轮播效果
    Java命名规范(简略)
    关于ngclass中添加多个样式类的解决方案
    python 常用标准库
  • 原文地址:https://www.cnblogs.com/dancewithautomation/p/2416185.html
Copyright © 2020-2023  润新知