【问题标题】:What is the closest Windows equivalent to the POSIX wait mechanism?与 POSIX 等待机制最接近的 Windows 是什么?
【发布时间】:2015-12-12 14:21:55
【问题描述】:

Linux 支持“sys/wait.h”中定义的 POSIX 等待机制。 wait, waitid, waitpid 方法可用于在使用fork 创建的父进程和子进程之间交换状态信息。

Windows 既不提供(原生)fork 支持,也不提供 POSIX 等待机制。相反,还有其他方法可用于 spwan 子进程,即CreateProcess

当将使用 fork/wait 以 C 或 C++ 编写的 linux 应用程序移植到 Windows 时,在父进程中监视子进程的状态变化(即 WEXITED, WSTOPPED, WCONTINUED)最合适的 native* 方法是什么?

*native 意味着不使用 Windows 未附带或由 MS 以运行时环境形式直接提供的其他库、框架、程序(如 cygwin、minGW)。

编辑:根据 cmets 的要求,我确实提供了一些关于应该以伪代码形式解决什么问题的更多信息:

//creates a new child process that is a copy of the parent (compare 
//POSIX fork()) and returns some sort of handle to it.
function spawnChild() 

// returns TRUE if called from the master process FALSE otherwise
function master()

// return TRUE if called from a child process FALSE otherwise
function child()

// returns TRUE if child process has finished its work entirely, 
// FALSE otherwise.
function completelyFinished()

//sends signal/message "sig" to receive where receiver is a single 
//handle or a set of handles to processes that shall receive sig
function sendSignal(sig, receiver)

// terminates the calling process
function exit()

// returns a handle to the sender of signal "sig"
function senderOf(sig)

function masterprocess()
  master //contains handle to the master process
  children = {}   //this is an empty set of handles to child processes
  buf[SIZE]  //some memory area of SIZE bytes available to master process and all children
  FOR i = 0 TO n - 1
    //spawn new child process and at its handle to the list of running 
    //child processes.
    children <- children UNION spawnChild() 
  IF(master())
    <logic here>
    sendSignal(STARTWORKING, children) //send notification to children
    WHILE(signal = wait())  // wait for any child to respond (wait is blocking) 
      IF signal == IMDONE
        <logic here (involving reads/writes to buf)>
        sendSignal(STARTWORKING, senderOf(signal))
      ELSEIF signal == EXITED
        children <- children \ signal.sender //remove sender from list of children
  ELSEIF(child())
    WHILE(wait() != STARTWORKING);
       <logic here (involving reads/writes to buf)>
       IF completelyFinished()
         sendSignal(EXITED, master)
         exit()
       ELSE
         sendSignal(IMDONE, master)

【问题讨论】:

  • Windows 进程的“状态”与 POSIX 进程不同。如果您想等待进程退出,您可以使用 GetExitCodeProcess 进行轮询,也可以简单地将 WaitForSingleObject(或其亲属之一)与进程句柄一起使用。
  • 所以你真的对进程间通信感兴趣,根本不关心 Posix 信号?
  • “任何与所述 POSIX 信号的行为最相似的适当机制”。真的没有什么接近的。 Win32 不能那样工作。你真正想要解决的问题是什么?
  • @norritt 但是你的程序没有做kill(parentPid, SIGCONT) 因为它作为一个信号进来,而不是你waitid 的东西。我试图了解原始 unix 程序是如何设计的,以便孩子自己生成停止、继续和退出事件,以便与父母交流。这对我来说是奇怪的部分。 Windows 的方式是使用 IPC 机制,而不是试图将 IPC 塞入子进程生命周期通知中。
  • 您正试图将方形钉 (POSIX) 放入圆孔 (Win32)。如果你足够用力的话,你可以把它放进去,但这需要大量的工作,而且你不会得到最好的结果。

标签: c windows monitoring wait status


【解决方案1】:

在回答实际问题之前,我将推荐一个更好的解决方案:您应该考虑简化父子关系。

基于伪代码,父母和孩子之间的信号是cross-process mutex的粗略形式,即他们所做的只是阻止这里的代码:

  IF signal == IMDONE
    <logic here (involving reads/writes to buf)>
    sendSignal(STARTWORKING, senderOf(signal))

同时运行多个实例。相反,&lt;logic here&gt; 应该被移动到相应的子进程中,由互斥锁保护,以便一次只有一个子进程可以运行它。

此时,父母需要做的就是启动孩子并等待他们全部退出。这在 Windows 中很容易通过等待进程句柄来完成。

(我想现代 POSIX 也支持某种比信号更复杂的跨进程互斥锁。)


还值得重新考虑您是否真的需要多个进程。多线程效率会更高,如果代码写得好,适配应该不难。


尽管如此,如果出于某种原因您绝对必须保留尽可能多的原始程序结构,pipes 可能会成为您的最好的选择。

  • 发送信号变成写一个字节。

  • 在孩子中,等待来自父母的信号变成了读取单个字节。

  • 在父节点中等待来自任何子节点的消息有点棘手。它仍然是单字节读取(针对每个孩子),但您需要使用overlapped I/O,如果您需要支持超过 64 个孩子,请使用IOCP

(或者,您可以使用多个线程,但这可能涉及太多的结构更改。)

  • 如果管道实现正确,当子节点退出或死亡时,父节点中的相应读取操作将终止并出现ERROR_BROKEN_PIPE 错误。因此,无需单独的机制来监控儿童的健康状况。

在这种情况下,我认为anonymous pipes 将是最合适的选择。这些是单工的,因此每个孩子需要两个管道。您可以将管道句柄的子端作为子进程的标准输入和输出传递。

对于匿名管道,您需要确保在每个子节点启动后关闭父节点的句柄副本,并且每个子节点仅继承与其自己管道对应的句柄。如果有任何额外的句柄在其管道的子端打开,则父级将不会在子级退出时收到任何通知。


这些都不是特别复杂,但请注意命名管道 I/O 有一点学习曲线。异步 I/O 更是如此,特别是如果您来自 UNIX 背景。请特别注意,要使用异步 I/O,您发出一个操作然后等待它完成,这与 UNIX 模型相反,您等待 I/O 准备好然后发出操作。

【讨论】:

  • 非常好,详细的答案,这是迄今为止最有帮助的,因为您一直关注进程应该相互通信而不是线程的要求。您对 Windows 和 POSIX 方式之间差异的阐述也很有帮助。对于感兴趣的读者,我想指出named pipes 也可用于 POSIX 系统。我相信有另一种方法可以通过结合CreateFileMappingCreateProcessCreateEventWaitForMultipleObjects/WaitForSingleObjectGetExitCodeProcess 来实现。
  • 关于您的假设“POSIX 还支持某种比信号更复杂的跨进程互斥锁”。是的,就像在 Windows 中一样,这可以通过 Semaphores 完成。
【解决方案2】:

如果您想向其他进程发送布尔条件信号,您可能应该为此使用共享事件。您可以按名称或通过句柄重复共享它们。您可以拥有任意数量的这些信号。例如,您可以为每个WEXITED, WSTOPPED, WCONTINUED 设置一个。

看到您的编辑:活动非常适合。在父级中创建命名事件并将它们的名称传递给子级命令。这样父母和孩子就可以互相发信号了。

您还需要共享内存部分,例如通过内存映射文件。这将对应于您代码中的buf

【讨论】:

  • 对象管理器允许您获得尽可能多的事件。当系统资源耗尽或调用者达到其配额限制时,您无法创建事件对象。这不一定是“随心所欲地 [...]”.
  • @IInspectable 是真的......你看到他想要创建数百万个事件的风险吗?我认为更像是 3。
  • 您不能创建无限数量的任何东西。我认为这段文字不能被合理地解释为那个意思。
  • @usr 我猜如果程序按预期终止,与 CreateEvent 共享的事件可能会起作用。程序也可能崩溃,被 Ctrl+C 或任务管理器杀死。在这种情况下,POSIX 系统仍会通知主线程。自定义事件可能不是这种情况见WIFEXITED, WEXITSTATUS, WIFSIGNALED, WCOREDUMP...。 Windows 中处理此类情况的最佳做法是什么?是否有适当的预定义事件可用,或者是否有可用的可挂钩回调可用于在终止时调用自定义退出事件?
  • 在 Windows 中,许多句柄是可等待的。进程句柄会在进程退出时发出信号。您可以等待进程终止。您可以使用 WaitForMultipleObjects 一次等待多个。我相信在较新的 Windows 版本中,当句柄发出信号时,您可以让 Windows 向线程池发布回调。
【解决方案3】:

您所拥有的似乎是一个工作队列安排,其中您有一个生产者进程和一堆工作进程。目前尚不清楚您是否将共享内存仅用作工作队列,或者您的工作人员是否在共享内存上进行操作(可能是一个巨大的矩阵或向量问题)。

在 Win32 中,您可能不会将其实现为单独的进程。

您将使用一组已经共享内存(相同地址空间)的生产者/消费者线程,并使用信号量或条件变量实现工作队列。

事实上,您可能会使用更高级别的抽象,例如QueueUserWorkItem。这使用默认的 Windows 线程池,但您可以使用 CreateThreadpool 创建自己的线程池。

【讨论】:

  • 这实际上是一个好点。我盲目地认为需要多个进程。 OP应该澄清。
  • 在这种情况下线程不是一个选项。虽然还有一个POSIX threads standard,但我有目的地询问一种明确涉及单独进程not 线程的方法。正如我最初的问题中所述,Windows 还支持使用CreateProcess 生成类似分叉的进程。
  • @norritt:每个进程都是从进程内部创建的。确实没有其他方法可以做到这一点,因此这并不是fork 的真正独特功能,而且肯定不会使CreateProcess “类似fork”CreateProcessfork 之间的主要区别在于,前者创建一个新的进程对象,而后者创建一个现有进程的副本(包括其所有拥有的资源)。 Windows API 中没有类似于 fork 的内容。
  • @norritt:您的问题与forking 无关(甚至不相关)。它正在寻求跨流程边界实现 IPC 和同步的方法。也许你需要修改你的问题来问你真正想要解决的问题。在 Windows 中要求 "fork-like" 是徒劳的:在 Windows 中没有任何 "fork-like"
  • 为什么要使用单独的进程?如果是为了进程隔离,那么您已经使用共享内存将自己击倒了。如果是为了高效使用多个 CPU,只需使用线程即可。多进程是(pre-pthreads)POSIX 范式。多线程在 Win32 编程中比较常见。
猜你喜欢
  • 2010-11-02
  • 2013-08-06
  • 2012-09-03
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
  • 2010-10-26
  • 1970-01-01
相关资源
最近更新 更多