【问题标题】:Pascal - zombie process remains after forkPascal - 分叉后仍然存在僵尸进程
【发布时间】:2015-01-18 14:49:46
【问题描述】:

我使用fpFork() 调用编写了一个多线程 tcp 服务器。工作正常,但在客户端断开连接后,僵尸进程仍然存在。有一个无限循环,我等待传入连接,fork,将此连接传递给孩子,孩子会处理它然后退出。但是,在父节点终止之前,子节点仍然是僵尸。

while True do
  begin
  // Accept connection
  ClientAddrLen := sizeof(ClientAddr);
  ClientSock := fpaccept(ServerSock, @ClientAddr, @ClientAddrLen);

  if ClientSock > 0 then // Success?
    begin

      Pid := fpFork();
      // Error fork
      if pid < 0 then
        begin
          CloseSocket(ClientSock);
          continue;
        end
      // Child process
      else if pid = 0 then
        begin
          CloseSocket(ServerSock);
          handleClient(ClientSock);
          CloseSocket(ClientSock);
          Halt(0);
        end
      // parent process
      else if pid > 0 then
        begin
          CloseSocket(ClientSock);
          continue;
        end;
    end;
end;

我对函数 fpWait()fpWaitPid() 感兴趣,但免费的 pascal 文档缺少示例,谷歌搜索它毫无价值,因此我什至不知道如何使用它。 我在 FreeBSD 上使用 fpc 2.6.4。

更新 1 经过一些反复试验、阅读手册和讨论后,我尝试了一些组合。我将以下函数放在已执行代码的父部分中:

else if pid > 0 then
  begin
    CloseSocket(ClientSock);
    //here
    continue;
  end;

a) 使用WaitProcess(pid)(等同于fpWaitPid(pid, @Status, 0)

使用此父进程等待子进程,但是由于父进程正在等待,它无法接受另一个连接,直到子进程终止。

b) 使用fpWaitPid(pid, @Status, WNOHANG)

这个函数不会阻塞程序,但它不会做任何事情。就像功能甚至不存在一样。这很令人困惑,因为在我读到的任何地方(它是用 C 编码的,但没关系,这些函数只是 unix 调用的包装器)都建议使用这个。

在这一点上,我不知道可能出了什么问题。提前谢谢你。

【问题讨论】:

  • 所有这些 fp* 函数都是带有“fp”前缀的普通 unix 函数,以避免冲突(如读取、写入和打开).. fpwaitpid 是一个围绕等待循环的常见原因的包装器,来自理查德史蒂文斯iirc。
  • @MarcovandeVoort 好点,我做了一些反复试验,但似乎没有任何效果。我会在我的问题中解释它
  • 我不是真正的分叉服务器专家(我更喜欢线程方法),但我会仔细检查你的场景是否匹配分叉服务器骨架,也许需要复制句柄或其他一些需要的额外处理。
  • @MarcovandeVoort Treads 可能是比这更好的解决方案,但我完全不熟悉 Pascal 中的线程。如果你能指出我正确的方向。
  • 对于基于线程的套接字,逻辑方向是重用或至少看看一些现成的套接字套件。主要是 indy 和 synapse,在较小程度上是不可移植的 ICS(也存在于 Delphi 中)。

标签: fork pascal freepascal zombie-process


【解决方案1】:

当子进程结束时,它变成了僵尸,系统向父进程发送SIGCHLD 信号。父进程在接受SIGCHLD 信号时将调用wait(freepascal 中的fpwait)函数。如果父进程调用wait函数系统将子进程的退出代码发送给父进程并删除它的记录并释放它的pid。

如果父进程没有对SIGCHLD 信号做出反应并且(或)不调用等待函数,则僵尸进程的数量将成倍增加,直到父进程终止。

procedure DoSigChld;  cdecl;
var stat : longint;
begin
  fpwait(stat);
end;

SignalAction.sa_handler := @DoSigChld;
fpsigaction(SIGCHLD, @SignalAction, nil);

【讨论】:

  • 这是正确的答案,我不知道为什么它被否决了。请注意,将 SIGCHLD 操作设置为 SIG_IGN 也可以。见microhowto.info/howto/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 2013-04-11
相关资源
最近更新 更多