TCP 연결을 처리하기 위해 하위 프로세스를 포크하는 네트워크 데몬을 작성했습니다. SIGINT
기본 프로세스 의 각 하위 프로세스에 대해 하나씩 트리거하여 kill
일부 최종 통계를 정리하고 수집합니다.
거의 모든 경우에 잘 작동하며 하위 프로세스가 매우 빠르게 종료됩니다. 그러나 때로는 하위 프로세스가 짧은 시간 초과(예: 5초) 내에 종료를 거부하는 경우도 있습니다.
당시 무슨 일이 있었는지 전혀 모르므로 상황을 진단하기 위해 자세한 출력을 추가했습니다. netcat
연결을 열고 프로세스를 일시 중지하는 것을 발견했습니다 netcat
.때때로원인 효과.
효과를 재현할 수 있을 때 디버그 출력은 다음과 같습니다.
REST-server(cleanup_queue): deleting children
REST-server(cleanup_queue): deleting PID 23344 handling localhost:48114
child_delete: Killing child 23344
child_delete: killed child with PID 23344
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting up to 5 seconds for condition
_limited_wait(PID 23344 terminated): waiting 0.02 (of 5 remaining) seconds
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting 0.04 (of 4.98 remaining) seconds
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting 0.08 (of 4.94 remaining) seconds
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting 0.16 (of 4.86 remaining) seconds
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting 0.32 (of 4.7 remaining) seconds
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting 0.64 (of 4.38 remaining) seconds
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting 1.28 (of 3.74 remaining) seconds
(r1, r2) = (1, Interrupted system call)
_limited_wait(PID 23344 terminated): waiting 2.46 (of 2.46 remaining) seconds
(r1, r2) = (1, Interrupted system call)
child_delete: PID 23344 refused to terminate within 5s
failed to delete child PID 23344
이 경우 기다려야 하는 "조건"은 이 종료의 결과입니다.
sub {
my $r1 = kill(0, $child_pid);
my $r2 = $!;
print "(r1, r2) = ($r1, $r2)\n";
$r1 != 1 && $r2 == Errno::ESRCH;
}
따라서 예상되는 결과는 PID가 더 이상 존재하지 않기 때문에("권한 거부" 때문이 아니라) 기본 프로세스가 PID를 "종료"할 수 없다는 것입니다.
그러나 어떤 이유로 "시스템 호출이 중단되었습니다"라는 메시지가 반복적으로 나타납니다.
기본 프로세스는 다음과 같은 신호 처리기를 사용합니다.
$SIG{'INT'} = $SIG{'TERM'} = sub ($) {
my $signal = 'SIG' . $_[0];
my $me = "signal handler[$$, $signal]";
print "$me: cleaning up\n"
if ($verbose > 0);
cleanup();
print "$me: executing default action\n"
if ($verbose > 1);
$SIG{$_[0]} = 'DEFAULT';
kill($_[0], $$); # execute default action
};
하위 프로세스가 분기되면 다음과 같이 신호 처리기를 재설정합니다.
sub child_create($)
{
my ($child) = @_;
my $pid;
reaper(0); # disable for the child
if ($pid = fork()) { # parent
reaper(1); # enable for the parent
} elsif (defined($pid)) { # child
my ($child_fun, @child_param) = @$child;
my $ret;
# prevent double-cleanup
$SIG{'INT'} = $SIG{'TERM'} = $SIG{'__DIE__'} = 'DEFAULT';
$ret = $child_fun->(@child_param);
exit($ret); # avoid returning from function call
} else { # error
print STDERR "child_create: fork(): $!\n";
}
return $pid;
}
reaper()
지금 막 손잡이 SIGCHLD
.
나타나는 효과의 원인은 무엇입니까? 하위 프로세스는 기본적으로 실행 while (defined(my $req = $conn->get_request)) {...}
(사용 HTTP::Daemon
)하므로 입력을 기다려야 합니다 netcat
.
추가 정보
만약을 대비해 OS는 VMware에서 실행되는 SLES12 SP5(Perl 5.18.2 사용)입니다.
메인 서버 루프의 코드는 다음과 같습니다:
while (defined(my $conn = $daemon->accept) || $! == Errno::EINTR) {
my $errno = $!;
if ($quit_flag != 0) {
last;
}
if ($errno == Errno::EINTR) {
next;
}
#... handle $req->uri->path()
}