无需将 $SIG{CHLD} 设置为 IGNORE 或自定义信号处理程序即可获取子级

发布于 2024-09-13 14:26:38 字数 936 浏览 2 评论 0原文

我正在尝试编写一个为每个连接分叉的套接字服务器。除了一个小警告之外,我已经成功了:我的子进程使用 Net:OpenSSH->capture2() ,这要求 $SIG{CHLD} 不得设置为 IGNORE 或自定义信号处理程序。如何在不设置信号处理程序或使用 wait 或 waitpid 减慢父进程的情况下收割我的子进程?

这是我的服务器代码:

my $sock = new IO::Socket::INET (
    LocalHost   =>  'localhost',
    LocalPort   =>  '1337',
    Proto       =>  'tcp',
    Listen      =>  SOMAXCONN,
    Reuse       =>  1,
); 
die "Could not create socket: $!\n" unless $sock;

my $new_client, $pid;

while($new_client = $sock->accept()){

    next if $pid = fork;
    die "fork: $!" unless defined $pid;

    close $sock;

    while(<$new_client> ) {
        #do Net::OpenSSH stuff
    }

    exit;

} continue {
    close $new_client;
}

如果我使用上面所示的代码,一切正常,但最终会出现一堆僵尸进程。如果我添加

local $SIG{CHLD} = 'IGNORE';

僵尸被收割,但 Net::OpenSSH->capture2() 方法调用的返回代码很混乱。我假设我的信号处理程序正在干扰 Net::OpenSSH 需要正常工作的某些自定义处理程序?

I am trying to write a socket server that forks for every connection. I have been successful except for one small caveat: my child processes use Net:OpenSSH->capture2() which requires that $SIG{CHLD} not be set to IGNORE or to a custom signal handler. How can I reap my children without setting the signal handler or slowing down the parent process with wait or waitpid?

Here is my server code:

my $sock = new IO::Socket::INET (
    LocalHost   =>  'localhost',
    LocalPort   =>  '1337',
    Proto       =>  'tcp',
    Listen      =>  SOMAXCONN,
    Reuse       =>  1,
); 
die "Could not create socket: $!\n" unless $sock;

my $new_client, $pid;

while($new_client = $sock->accept()){

    next if $pid = fork;
    die "fork: $!" unless defined $pid;

    close $sock;

    while(<$new_client> ) {
        #do Net::OpenSSH stuff
    }

    exit;

} continue {
    close $new_client;
}

If I use the code as shown above, everything works but I end up with a bunch of zombie processes. If I add

local $SIG{CHLD} = 'IGNORE';

the zombies are reaped, but the Net::OpenSSH->capture2() method call has a messed up return code. I'm presuming my signal handler is interfering with some custom handler that Net::OpenSSH needs to work properly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

唐婉 2024-09-20 14:26:38

继续在父进程中设置 SIGCHLD 处理程序,但在子进程中禁用它 - 例如在 fork 之后立即放置一个 local $SIG{CHLD}称呼。

在子进程中,SIGCHLD 事件来自 Net::OpenSSH 方法,并且 Net::OpenSSH 模块将处理这些事件。

在父进程中,SIGCHLD 事件来自子进程的退出。这些正是您感兴趣的事件,也是您需要处理以防止僵尸发生的事件。

Go ahead and set up a SIGCHLD handler in the parent process, but disable it in the child processes -- for example put a local $SIG{CHLD} immediately after the fork call.

In the child processes, SIGCHLD events come from the Net::OpenSSH methods, and the Net::OpenSSH module will handle those events.

In the parent process, SIGCHLD events come from your child processes exiting. Those are exactly the events you are interested in and the ones you need to handle to prevent zombies.

战皆罪 2024-09-20 14:26:38

如果您不需要忽略子进程并在同一进程中使用 Net::OpenSSH,那么您可以在不使用 Net:: 的进程中使用 $SIG{CHLD} = 'IGNORE' OpenSSH(例如,分叉“自动获取”子进程的单个服务器进程)并在使用 Net::OpenSSH 的进程(例如,子进程)中将其重置为 $SIG{CHLD} = 'DEFAULT'服务器的)。


或者,您可以在在每个新的客户端连接后循环。你仍然可能会遇到一个或多个僵尸在周围徘徊,但它们将在下一次连接时全部被收割。如果您切换到使用 select (或类似 IO::Select),您可以通过以下方式设置僵尸的“生命周期”上限在监听套接字上进行选择,并在每次超时返回以及每次“套接字就绪”返回后进行一轮非阻塞僵尸收割。

来自 perlfunc 手册页

如果你说

使用 POSIX ":sys_wait_h";
#...
做 {
    $kid = waitpid(-1, WNOHANG);
而 $kid > 0;

然后你可以对所有挂起的僵尸进程进行非阻塞等待。非阻塞等待在支持 waitpid(2) 或 wait4(2) 系统调用的机器上可用。

If you never need to ignore children and use Net::OpenSSH in the same process then you might be able to use $SIG{CHLD} = 'IGNORE' in the processes that do not use Net::OpenSSH (e.g. the single server process that forks off “auto-reaped” children) and reset it to $SIG{CHLD} = 'DEFAULT' in the processes that use Net::OpenSSH (e.g. the children of the server).


Alternatively, you could use a non-blocking waitpid in a loop at after each new client connection. You can still end up with one or more zombies hanging around, but they will all be reaped at the next connection. If you switch to using select (or something like IO::Select), you could set an upper bound on the “lifetime” of the zombies by doing a select on your listening socket and doing a round of non-blocking zombie reaping after every timeout return as well as every “socket ready” return.

From the waitpid section of perlfunc manpage:

If you say

use POSIX ":sys_wait_h";
#...
do {
    $kid = waitpid(-1, WNOHANG);
} while $kid > 0;

then you can do a non-blocking wait for all pending zombie processes. Non-blocking wait is available on machines supporting either the waitpid(2) or wait4(2) syscalls.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文