Thread.Abort()是如何工作的?我们通常在将无效输入传递给方法或对象即将进入无效状态时抛出异常。让我们考虑以下示例privatevoidSomeMethod(stringvalue){if(value==null)thrownewArgumentNullException("value");//方法逻辑在这里}在上面的示例中,我插入了一个throw语句。我的问题是如何设置运行时抛出ThreadAbortException。显然throw语句在所有方法中都是不可能的,即使运行时设法在我们的自定义方法中抛出ThreadAbortException。我想知道他们是怎么做到的?我很好奇想知道幕后发生了什么,我打开了一个反射器来打开Thread.Abort并以这个[MethodImplAttribute(MethodImplOptions.InternalCall)]privateexternvoidAbortInternal();//在CLR中实现然后我用谷歌搜索并结束找到了ThreadAbortException的真正工作原理。Thislink说运行时通过QueueUserAPC函数发出APC,他们就是这样做的。我不知道QueueUserAPC方法,我只是想看看我是否可以使用一些代码。以下代码显示了我的尝试。[DllImport("kernel32.dll")]staticexternuintQueueUserAPC(ApcDelegatepfnAPC,IntPtrhThread,UIntPtrdwData);委托无效ApcDelegate(UIntPtrdwParam);线程t=新线程(Threadproc);t.开始();//等待线程启动uintresult=QueueUserAPC(APC,newIntPtr(nativeId),(UIntPtr)0);//返回零(失败)interror=Marshal.GetLastWin32Error();//错误也为零privatestaticvoidAPC(UIntPtr数据){Console.WriteLine("回调调用");}privatestaticvoidThreadproc(){//someinfiniteloopwithasleep}如果我做错了什么,请原谅我,我不知道该怎么做。回到问题上来,了解这方面知识的人或CLR团队的部分成员能否解释一下它在内部是如何工作的?如果APC是技巧运行时,我在这里做错了吗?您确定阅读了您指向的页面吗?最后它归结为:对Thread.Abort的调用归结为.NET在要中止的线程上设置一个标志,然后在线程生命周期的某些点检查该标志,如果设置了标志则抛出异常.要使APC回调起作用,您需要一个线程句柄(与线程ID不同)。我还更新了PInvokes的属性。还要记住,线程需要处于“警报”等待状态才能调用APC(Thread.Sleep会告诉我们)。因此,如果线程正忙于执行操作,则可能不会调用它。[DllImport("kernel32.dll",EntryPoint="GetCurrentThread",CallingConvention=CallingConvention.StdCall)]publicstaticexternIntPtrGetCurrentThread();[DllImport("kernel32.dll",EntryPoint="QueueUserAPC",CallingConvention=CallingConvention.StdCall,SetLastError=true)]publicstaticexternuintQueueUserAPC(ApcDelegatepfnAPC,IntPtrhThread,UIntPtrdwData);[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]publicdelegatevoidApcDelegate(UIntPtrdwParam);[DllImport("kernel32.dll",EntryPoint="DuplicateHandle",CallingConvention=CallingConvention.StdCall,SetLastError=true)]publicstaticexternboolDuplicateHandle([In]System.IntPtrhSourceProcessHandle,[In]System.IntPtrhSourceHandle,[在]]System.IntPtrhTargetProcessHandle,输出System.IntPtrlpTargetHandle,uintdwDesiredAccess,[MarshalAsAttribute(UnmanagedType.Bool)]boolbInheritHandle,uintdwOptions);[DllImport("kernel32.dll",EntryPoint="GetCurrentProcess",CallingConvention=CallingConvention.StdCall,SetLastError=true)]publicstaticexternIntPtrGetCurrentProcess();staticIntPtrhThread;publicstaticvoidSomeMethod(objectvalue){DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),出hThread,0,false,2);while(true){Console.WriteLine(".");Thread.Sleep(1000);}}privatestaticvoidAPC(UIntPtrdata){Console.WriteLine("调用回调");}staticvoidMain(string[]args){Console.WriteLine("inMainn");Threadt=newThread(Program.SomeMethod);t.Start();Thread.Sleep(1000);//等到线程填充hThread成员——不要在家里这样做,这不是同步线程的好方法……uintresult=QueueUserAPC(APC,hThread,(UIntPtr)0);Console.ReadLine();}编辑:CLR如何注册异常给定线程数的这个循环:while(true){i=((i+7)*3^0x73234)&0xFFFF;}然后我.Abortthreadandviewnativestacktrace...ntdll!KiUserExceptionDispatcherKERNELBASE!RaiseExceptionclr!RaiseComPlusExceptionclr!RedirectForThrowControl2clr!RedirectForThrowControl_RspAlignedclr!RedirectForThrowControl_FixRspcsTest.Program.SomeMethod(System.ObjectThrow)调用RedirectForThrow的地址它指向我的中间循环,没有跳转或调用:nopmoveax,dwordptr[rbp+8]addeax,7//代码流将返回执行这一行leaeax,[rax+rax*2]xoreax,73234handeax,0FFFFhmovdwordptr[rbp+8],eaxnopmovbyteptr[rbp+18h],1jmp000007fe`95ba02da//循环回到顶部正常流量。他们显然需要提供几个包装器来修复和恢复所有堆栈寄存器以使其正常工作(因此适当命名为_FixRsp和_RspAlignedAPI。在一个单独的测试中,我只是在我的线程循环中调用了Console.Write(),看起来CLR在物理调用WriteFile之前注入了一个测试:KERNELBASE!RaiseExceptionclr!RaiseTheExceptionInternalOnlyclr!??::fnodobfm::`'字符串'clr!Helpermethodframe::pushSlowHelperclr!IO..SafeHandles.SafeFileHandle,Byte[],Int32,Int32,Boolean)要使QueueUserAPC工作,您必须做两件事。获取目标线程句柄。请注意,这与本机线程ID不同。允许目标线程进入可警报状态。这是一个完整的程序来演示这一点。类程序{[DllImport("kernel32.dll",EntryPoint="DuplicateHandle",CallingConvention=CallingConvention.StdCall,SetLastError=true)]publicstaticexternboolDuplicateHandle([In]System.IntPtrhSourceProcessHandle,[In]System.IntPtrhSourceHandle,[输入]System.IntPtrhTargetProcessHandle,输出System.IntPtrlpTargetHandle,uintdwDesiredAccess,[MarshalAsAttribute(UnmanagedType.Bool)]boolbInheritHandle,uintdwOptions);[DllImport("kernel32.dll",EntryPoint="GetCurrentProcess",CallingConvention=CallingConvention.StdCall,SetLastError=true)]publicstaticexternIntPtrGetCurrentProcess();[DllImport("kernel32.dll")]privatestaticexternIntPtrGetCurrentThread();[DllImport("kernel32.dll")]privatestaticexternuintQueueUserAPC(ApcMethodpfnAPC,IntPtrhThread,UIntPtrdwData);私人委托voidApcMethod(UIntPtrdwParam);staticvoidMain(string[]args){Console.WriteLine("Main:"+Thread.CurrentThread.ManagedThrea做过);IntPtrthreadHandle=IntPtr.零;varthreadHandleSet=newManualResetEvent(false);varapcSet=newManualResetEvent(false);varthread=newThread(()=>{Console.WriteLine("threadstarted");threadHandle=GetCurrentThread();DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),outthreadHandle,0,false,2);threadHandleSet.Set();apcSet.WaitOne();for(inti=0;ithis本质上允许开发人员将方法的执行注入任意线程目标线程不必像消息泵一样传统方法。但是,这种方法的一个问题是目标线程必须处于可警报状态。因此基本上线程必须调用固定的.NET阻塞调用,如Thread.Sleep、WaitHandle.WaitOne等,以便执行APC队列。这很容易,底层操作系统可以做到。如果线程处于“在另一个核心上运行”以外的任何状态,则没有问题-它的状态设置为“不再运行”。如果线程在另一个核心上运行,操作系统硬件中断了另一个核心。它是一个处理器间驱动程序,所以线程被消灭了。只要提到“时间片”、“量子”等……我就下载了SSCLI代码并开始探索。代码对我来说很难理解(主要是因为我不是C++或ASM专家),但我确实看到了很多钩子,半同步注入中止。仅举几个。我想知道的是如何注入异步中止。劫持指令指针的一般想法是它如何发生的一部分。但是,它比我上面描述的要复杂得多。Suspend-Modify似乎并不总是使用-Resume成语。从SSCLI代码中我可以看出它确实在某些情况下暂停和恢复线程以准备劫持,但情况并非总是如此。在我看来,线程运行时也可以进行劫持。您链接的文章提到在目标线程上设置中止标志。这在技术上是正确的。该标志称为TS_AbortRequested并且有很多逻辑控制如何设置该标志。检查以确定是否存在受约束的执行区域以及线程当前是否处于try-catch-finally-fault块中。其中一些工作涉及堆栈爬行,这意味着必须暂停和恢复线程。然而,真正神奇的地方在于如何检测标志更改。这篇文章没有很好地解释这一点。我在上面的列表中提到了几个半同步注入点。理解这些应该是微不足道的。但是异步注入是如何发生的呢?好吧,在我看来,JIT是幕后的向导。JIT/GC中内置了某种轮询机制,可以定期确定是否应该进行回收。这也提供了检查任何托管线程是否已更改状态(例如设置中止标志)的机会。如果设置了TS_AbortRequested,就会发生劫持。如果您正在查看SSCLI代码,这里有一些不错的功能。还有很多其他的线索。请记住,这是SSCLI,因此方法名称可能与生产中观察到的调用堆栈不完全匹配(正如JoshPoley发现的那样),但会有相似之处。此外,很多多线程劫持都是用汇编代码完成的,因此有时很难理解。我突出显示了JIT_PollGC,因为我相信这是有趣的事情发生的地方。这是我认为JIT将动态地和战略性地放入执行线程的钩子。这基本上就是那些紧密循环仍然可以接收中止注入的机制。目标线程实际上主要是轮询中止请求,但作为调用GC1的更大策略的一部分很明显,JIT、GC和线程中止密切相关。当您查看SSCLI代码时,这是显而易见的。例如,用于确定线程中止安全点的方法与用于确定是否允许GC运行的方法相同。1共享源CLIEssentials,DavidStutz,2003年,第3页。249-250以上是C#学习教程:Thread.Abort()是如何工作的?如果分享的所有内容对您有用,需要了解更多C#学习教程,希望您多多关注---本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
