【问题标题】:How is Ctrl-C message delivered to a process running on windows?Ctrl-C 消息如何传递到 Windows 上运行的进程?
【发布时间】:2011-12-04 16:27:38
【问题描述】:

我创建了一个 C# 控制台应用程序,通过订阅 Console.CancelKeyPress 事件来处理 Ctrl-C。当我执行(调试版本)应用程序时,进程中已经创建了 14 个线程。当我按下 Ctrl-C 时,创建了一个新的第 15 个线程并调用了我的处理程序。我使用 sysinternals 的进程资源管理器来查看进程的状态。

我很想知道 Ctrl-C 消息如何传递给进程以及如何创建附加线程的内部机制?我想,即使我不订阅事件,它仍然会创建一个额外的线程并退出进程。处理 Ctrl-C 的默认机制是如何为应用程序设置的。

我是一名 .net 开发人员,但想了解 Windows 操作系统的幕后工作原理。上面的问题只是出于学习windows操作系统的好奇。

【问题讨论】:

    标签: windows console-application signals ctrl


    【解决方案1】:

    当Windows需要通知一个外部事件的控制台程序时,没有窗口消息循环可以发送通知,因此Windows将在目标进程中create a thread执行任何定义的回调函数。 CTRL+C 事件的默认处理程序只调用ExitProcess,但挂钩CancelKeyPress 事件会使用处理程序函数调用Win32 的SetConsoleCtrlHandler 函数。

    handler function 的文档解释了它的工作原理:

    与 SetConsoleCtrlHandler 函数一起使用的应用程序定义的函数。控制台进程使用此函数来处理进程接收到的控制信号。收到信号后,系统在进程中创建一个新线程来执行函数。

    请注意,Windows 注入到您的进程中的线程具有相当小的堆栈,因此 CLR 处理程序例程实际上将 Threadpool 工作项排队以执行您的事件处理程序。这意味着 Windows 注入的线程一个工作线程都可能被创建,导致您在处理 CTRL+C 事件期间看到最多 2 个额外的线程。

    【讨论】:

    • +1 - 有用。在没有消息循环的情况下,新线程如何注入进程?
    • @AnandPatel:CreateRemoteThread API 在另一个进程中创建一个新线程。
    • 将您的回复设置为对我的问题的回答。我很重视 Hans Passant 的回应,但你在我寻找的信息中是准确的。你能否分享更多关于为什么注入的线程有一个小堆栈的信息。为什么不像其他 .net 线程那样默认 1 MB 线程堆栈大小?
    • 我找到了解释堆栈可能太小而无法在 64 位系统上运行异常处理程序的文档,但我没有看到任何说明堆栈为何如此小的内容。我想这个想法是使用尽可能少的资源。您是否希望 CTRL+C 不起作用,因为您的地址空间中没有可用的连续 1MB 块?
    • 我在 CancelKeyPress 事件处理程序中设置了一个断点,并调用 AppDomain.GetCurrentThreadID() 来获取非托管线程 ID。我在 VMMap 中查找了该线程,发现该线程的保留和提交的堆栈大小为 1MB。与线程池线程相比,我不确定它是如何小堆栈大小的线程。我想,线程池线程将具有相同的大小。我在 32 位系统上做了上述实验。
    【解决方案2】:

    是的,Windows 启动一个线程来调用SetConsoleCtrlHandler() 注册的处理程序。它由名为 ControlCHooker 的小型内部辅助类的 Hook() 方法调用。由 Cancel.CancelKeyPress 事件的 add() 访问器调用。 Windows 回调使您的事件处理程序运行。

    Reflector 或 ILSpy 等优秀的反汇编程序以及参考源可以帮助您发现这些实现细节。

    【讨论】:

    • 我知道 CLR 创建的线程很少用于做家务。例如进行垃圾收集的线程。如果我没记错的话,这些线程是在应用程序开始时创建的,而不是在正在运行的应用程序中间。我想知道新线程(用于处理 Ctrl-C)是如何在进程中注入/创建的?
    • 不,CLR 和 Windows 都根据需要即时创建线程。 CreateThread() 和 QueueUserWorkItem() 是主要的 winapi 函数。 Marshal.GetFunctionPointerForDelegate() 在低级别支持获取由 Windows 创建的线程来执行托管代码。
    猜你喜欢
    • 2015-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    • 2014-08-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多