PID 번호를 사용하여 프로세스를 종료할 때 잘못된 프로세스 종료를 방지하는 방법은 무엇입니까?

PID 번호를 사용하여 프로세스를 종료할 때 잘못된 프로세스 종료를 방지하는 방법은 무엇입니까?

Bash 스크립트에서 장기 실행 백그라운드 프로세스를 시작합니다. 프로세스를 백그라운드로 보낸 후 PID 번호를 변수에 저장하고 필요한 경우 해당 PID 번호를 사용하여 프로세스를 종료합니다.

그러나 스크립트가 백그라운드 프로세스를 종료하기 전에 백그라운드 프로세스가 종료되고 시스템이 새로 생성된 프로세스에 동일한 PID 번호를 할당하는 경우 해당 번호를 사용하여 백그라운드 프로세스를 종료할 때 작동할 수 있습니다. 새로 생성된 프로세스를 종료합니다(권한에 따라 다름). , 물론).

사용된 PID 번호는 단기간 동안 새로 생성된 프로세스에 할당되지 않는다는 것을 알고 있지만 내 스크립트는 몇 주 동안 실행되므로 가능합니다.

그러한 사고를 어떻게 피할 수 있습니까?

답변1

의견에서 제안한 대로 이 pkill유틸리티가 유용할 수 있습니다.

"bash 스크립트"라고 말했으므로 실행해야 할 가능성이 높습니다 pkill bash. 이는 해서는 안 되는 일입니다.

pkill -f <name>대신 전체 프로세스 이름을 사용하여 일치시키는 를 사용할 수 있습니다. 따라서 작업이 이라고 가정하면 bash /home/me/my_script.sh다음을 사용할 수 있습니다.

pkill -f -e my_script.sh

이것은 -e선택사항이며 무엇이 죽었는지 출력합니다.


선택하다:

다음 스크립트를 다른 이름으로 /usr/bin/mykill(또는 원하는 위치에) 저장합니다.

#!/bin/bash
mypid="$1"
if [[ ! -f /proc/$mypid/cmdline ]]; then
    echo "Process ID not found."
    exit 1
else
    echo "About to kill $(cat /proc/$mypid/cmdline)"
    echo "Press enter if you want to kill that process"
    read -p "Press CTRL-C if you don't want that"
    kill $mypid
fi

다음과 같이 실행하십시오.mykill <pid>

답변2

백그라운드 프로세스를 제어할 수 있는 경우 추가 ID를 명령줄에 레이블로 추가하고 Pid와 함께 사본을 보관한 다음 체크인할 수 있습니다 ps -o args myPid.

나는 이런 옵션을 사용한다--unique "${myTag}"

uuidgenmyTag를 , 또는 a에서 date나노초 정밀도로 내 보냅니다. SSH 작업인 경우 로컬 호스트 이름을 포함합니다.

새로운 옵션을 도입할 수 없는 경우:

date +%s.. 작업 시작 시간을 가져오는 데 사용되며 Pid와 함께 저장됩니다.

.. ps -o etimes프로세스의 경과 시간을 초 단위로 가져오는 데 사용됩니다.

.. 현재와 비교하면 date +%s(몇 초 정도 차이가 날 수 있음).

두 방법 모두 Pid와 결합할 때 무시할 수 있는 오류 확률을 가져야 합니다.

답변3

잘못된 PID가 삭제될 염려 없이 PID를 /proc/<PID>의 타임스탬프와 결합하여 고유 ID로 만들었습니다.

$PID 저장:

echo $PID $(stat --format %Z /proc/$PID/comm) > pid

$PID를 안전하게 종료합니다.

read PID TIMESTAMP < pid
[[ $(stat --format %Z /proc/$PID/comm) != $TIMESTAMP ]] || kill -SIGKILL $PID

이렇게 하면 다른 프로세스에서 $PID를 회수하더라도 생성 시간(/proc/$PID/comm의 타임스탬프)이 다르기 때문에 복제가 불가능합니다.

PS: 이는 true이면 아무 작업도 수행하지 않고, 그렇지 않으면 실행을 [[ ... ]] || cmd의미합니다 .[[ ]]cmd

편집: 다른 해결 방법이 많이 있지만 그냥 해결하면 안 되는 이유가 무엇인지 알아냈습니다. 이는 백그라운드 서비스가 필요하지 않고 대부분의 컨테이너에서 잘 지원되지 않는 systemd와 같은 고급 시스템 라이브러리가 필요하지 않은 가장 간단하고 직접적인 방법이어야 합니다. 저는 진행 중인 다른 프로세스를 중단해야 하는 제품에 이 접근 방식을 사용했습니다.

EDIT2: 백그라운드 프로세스를 종료할 때 별도의 프로세스 그룹(예: )에서 백그라운드 프로세스를 시작하는 것이 좋습니다 setsid background_process &. 그런 다음 background_process의 하위 프로세스에서 프로세스 그룹 ID를 가져와 프로세스 그룹 ID를 얻을 수 있습니다 ps -o pgid= $$. 그런 다음 킬러 End-kill 프로세스 그룹 ID에서 모든 하위 프로세스가 원자적으로 종료됩니다. 그렇지 않으면 일반 프로세스 ID를 종료하고 해당 하위 프로세스는 여전히 살아 있으며 pkill -P parant_pid이를 사용하더라도 새 하위 프로세스가 탈출할 가능성이 여전히 있습니다.

답변4

나는 이것이 일반적으로 불가능하다는 데 동의하지 않습니다. pkill피할 수 있다면 이름으로 죽이는 것을 권장하지 않습니다(특정 명령의 여러 인스턴스가 별도의 시간 초과를 갖도록 합법적으로 원하는 경우에는 어떻게 됩니까?). 문제의 답변이 어떻게 jobs경쟁 조건을 제거하고 원자적이 지 jobs않은지 이해하지 못합니다 kill.

그러나 작업 제어가 활성화된 경우 프로세스 그룹 ID( s )를 사용 하거나 옵션이 아닌 경우 PGID하위 쉘을 사용하고 프로세스 상위 ID( )로 종료할 수 있습니다. PPID내 게시물을 참조하세요:https://unix.stackexchange.com/a/649320/464414

선호하는 방법을 발췌하여 여기에 붙여넣겠습니다. 참고 사항과 대안은 위의 게시물을 참조하세요.

업데이트: 이제 다음 버전이 파이프라인에서 작동합니다.

timeOut() {
    checkArgs() { [ $(( ${1} )) -gt 0 -a "${*:2}" ]; }
    jobControlEnabled() { expr "${-}" : '.*m' >/dev/null; }
    terminalFDs() { [ -t 0 -a -t 1 ]; }
    groupLeader() { sh -c 'expr `ps -o pgid= ${PPID}` : "${PPID}" >/dev/null;'; }
    timeOutImpl() {
        groupLeader || { echo "Job control error - not group leader!"; return -1; }
        KILL_SUB="kill -- -`sh -c 'echo ${PPID}'`"
        { sleep ${1}; ${KILL_SUB}; } &
        "${@:2}"; ${KILL_SUB}
    }
    checkArgs "${@}" || { echo "Usage: timeOut <delay> <command>"; return -1; }
    if jobControlEnabled && terminalFDs; then
        ( timeOutImpl "${@}"; )
    else
        ( set -m; ( timeOutImpl "${@}"; ); )
    fi
}

관련 정보