Perl의 `kill`은 예상치 못한 `$! == Errno::EINTR`을 사용합니다.

Perl의 `kill`은 예상치 못한 `$! == Errno::EINTR`을 사용합니다.

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()
}

관련 정보