배시에서 SIGTERM을 자식에게 전달


86

Bash 스크립트가 있는데 다음과 유사합니다.

#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon

이제 스크립트를 실행하는 bash 쉘이 SIGTERM 신호를 수신하면 SIGTERM도 실행중인 서버로 보내야합니다. 가능합니까?

답변:


91

시험:

#!/bin/bash 

_term() { 
  echo "Caught SIGTERM signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGTERM

echo "Doing some initial work...";
/bin/start/main/server --nodaemon &

child=$! 
wait "$child"

일반적으로 bash자식 프로세스가 실행되는 동안 신호를 무시합니다. 로 서버 시작 &과 함께, 쉘의 작업 제어 시스템에 의지 배경을 $!서버의 PID를 잡고 (함께 사용되는 waitkill). wait그런 다음 호출 하면 지정된 PID (서버)로 작업이 완료 되거나 신호가 발생할 때까지 기다립니다 .

쉘이 수신 SIGTERM(또는 서버가 독립적으로 종료)하면 wait호출이 리턴됩니다 (서버의 종료 코드 또는 신호가 수신 된 경우 신호 번호 + 128로 종료). 이후 쉘이 SIGTERM을 수신하면 _term종료하기 전에 SIGTERM 트랩 핸들러로 지정된 함수 를 호출합니다 (여기서 정리를 수행하고을 사용하여 수동으로 신호를 서버 프로세스에 전파합니다 kill).


좋아 보인다! 나는 그것을 시험하고 그것을 시험 할 때 응답 할 것이다.
Lorenz

7
그러나 exec는 주어진 프로그램으로 쉘을 대체하는데 왜 후속 wait호출이 필요한지 잘 모르겠습니다 .
iruvar

5
1_CR의 요점이 유효하다고 생각합니다. 어느 쪽이든 당신은 단순히 사용 exec /bin/start/main/server --nodaemon(이 경우 쉘 프로세스는 서버 프로세스로 대체하고 당신은 어떤 신호를 전파 할 필요가 없습니다) 또는 사용 /bin/start/main/server --nodaemon &,하지만 exec정말 의미가 없습니다.
Andreas Veithen

2
자식이 종료 된 후에 만 ​​쉘 스크립트를 종료하려면 _term()기능 에서 wait "$child"다시 해야 합니다. 다시 시작하기 전에 셸 스크립트가 종료되기를 기다리는 다른 감독 프로세스가 있거나 EXIT정리를 수행하기 위해 트랩 되어 하위 프로세스가 완료된 후에 만 ​​실행되도록해야하는 경우에 필요할 수 있습니다 .
LeoRochael

1
@AlexanderMills 다른 답변을 읽으십시오. 찾고 exec있거나 트랩설정하려고합니다 .
스튜어트 P. 벤틀리

78

Bash는 현재 대기중인 프로세스에 SIGTERM과 같은 신호를 전달하지 않습니다. 당신이하여 스크립트를 종료 할 경우 에 segueing 서버, 사용한다 (직접 서버를 시작한 것처럼 그것이 신호와 다른 작업을 처리 할 수 있도록) exec할, 프로세스가 열릴와 쉘을 대체 :

#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon

당신이 어떤 이유로 주변의 껍질을 유지해야 할 경우의 조합을 사용한다 (예. 당신은 서버가 종료 한 후 몇 가지 정리를 할 필요) trap, waitkill. SensorSmith의 답변을 참조하십시오 .


이것이 정답입니다! 훨씬 더 간결하고 OP의 원래 요청을 정확하게 해결합니다
BrDaHa

20

Andreas Veithen 은 (OP의 예와 같이) 호출에서 돌아올 필요가 없으면 exec명령을 통한 호출만으로 충분하다고 지적합니다 ( @Stuart P. Bentley 's answer ). 그렇지 않으면 "전통적인" trap 'kill $CHILDPID' TERM(@cuonglm의 답변)은 시작이지만 wait트랩 처리기가 실행 된 후에 호출이 실제로 리턴되며 이는 자식 프로세스가 실제로 종료되기 전에 계속 될 수 있습니다. 따라서 "추가"호출 wait이 권장됩니다 ( @ user1463361의 답변 ).

이것은 개선이지만 여전히 경쟁 조건을 가지므로 프로세스가 절대 종료되지 않을 수 있습니다 (신호기가 TERM 신호 전송을 재 시도하지 않는 한). 취약점은 트랩 핸들러를 등록하고 자식의 PID를 기록하는 것입니다.

다음은이 취약점을 제거합니다 (재사용을 위해 기능으로 패키지).

prep_term()
{
    unset term_child_pid
    unset term_kill_needed
    trap 'handle_term' TERM INT
}

handle_term()
{
    if [ "${term_child_pid}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null
    else
        term_kill_needed="yes"
    fi
}

wait_term()
{
    term_child_pid=$!
    if [ "${term_kill_needed}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null 
    fi
    wait ${term_child_pid}
    trap - TERM INT
    wait ${term_child_pid}
}

# EXAMPLE USAGE
prep_term
/bin/something &
wait_term

2
탁월한 직업-여기에 지적하기 위해 답변의 링크를 업데이트했습니다 (이보다 포괄적 인 솔루션 임에도 불구하고 StackExchange UI가 스크립트를 수정 한 것에 대한 cuonglm의 답변으로 저를 인정하지 않습니다) 실제로가 해야하는 일을 하고 거의 모든 설명 텍스트 작성 , 영업 후 도 이해하지 못했다 ) 몇 가지 사소한 재 편집을했다합니다.
스튜어트 P. 벤틀리

2
@ StuartP.Bentley, 감사합니다. 나는 두 가지 (허용되지 않는) 답변과 외부 참조를 조립하는 것에 놀랐다. 그리고 나는 경쟁 조건을 낮추어야했다. 내가 제공 할 수있는 몇 가지 추가 링크로 링크에 대한 참조를 업그레이드 할 것입니다.
SensorSmith

3

대기 명령이 실제로 완료되기 전에 프로세스가 종료되었으므로 제공된 솔루션이 작동하지 않습니다. 그 기사 http://veithen.github.io/2014/11/16/sigterm-propagation.html 에서 마지막 스 니펫은 OpenShift에서 사용자 정의 sh 러너로 시작한 응용 프로그램의 경우 효과적입니다. Java 프로세스의 PID가 1 인 경우 불가능한 스레드 덤프를 얻을 수 있어야하므로 sh 스크립트가 필요합니다.

trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.