Windows 最接近 POSIX 等待机制的是什么?

What is the closest Windows equivalent to the POSIX wait mechanism?

Linux 支持 "sys/wait.h" 中定义的 POSIX 等待机制。方法 wait, waitid, waitpid might be used to exchange status information between parent and child processes that have been created using fork.

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

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

*native 表示不使用不随 windows 提供或由 MS 直接提供的额外库、框架、程序(如 cygwin、minGW)运行时环境。

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

//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)

如果您想向其他进程发送布尔条件信号,您可能应该为此使用共享事件。您可以按名称或处理重复来共享它们。您可以根据需要获得任意数量的这些信号。例如,您可以为每个 WEXITED, WSTOPPED, WCONTINUED.

看到您的编辑:活动非常适合。在 parent 中创建命名事件并将它们的名称传递给 children 之类的命令。这样 parent 和 child 可以互相发信号。

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

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

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

您将使用 producer/consumer 个线程的集合,这些线程已经在共享内存(相同地址 space),并且您将使用信号量或条件变量实现工作队列。

事实上,您可能会使用更高级别的抽象,例如 QueueUserWorkItem. This uses the default Windows thread pool, but you can create your own thread pool, using CreateThreadpool

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

根据伪代码,parent和children之间的信号是cross-process mutex的粗略形式,即它们所做的只是阻止这里的代码:

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

来自 运行同时多个实例。相反,<logic here> 应该移动到相应的 child 进程中,由互斥锁保护,这样一次只有一个 child 可以 运行 它。

到那时,parent 需要做的就是启动 children 并等待它们全部退出。这在 Windows 中很容易通过等待进程句柄来完成。

(我想现代 POSIX 也支持某种 cross-process 比信号更复杂的互斥量。)


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


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

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

  • 在 child 中,等待来自 parent 的信号变为读取单个字节。

  • 在 parent 中等待来自任何 children 的消息有点棘手。它仍然是 single-byte 阅读(每个 child),但您需要使用 overlapped I/O and, if you need to support more than 64 children, IOCP

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

  • 如果管道实现正确,当 child 退出或终止时,parent 中的相应读取操作将终止并出现 ERROR_BROKEN_PIPE 错误。因此不需要单独的机制来监控 children.
  • 的健康状况

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

对于匿名管道,您需要确保在每个 child 启动后关闭 parent 的句柄副本,并且每个 child只继承自己管道对应的句柄。如果 child 的管道末端还有任何其他句柄处于打开状态,则当 child 退出时 parent 将不会收到任何通知。


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