Ctrl + C로 취소 할 프로세스 제어


13

Linux로 부팅하고 작은 Bash 스크립트를 실행하는 라이브 CD가 있습니다. 이 스크립트는 두 번째 프로그램 (일반적으로 컴파일 된 C ++ 바이너리)을 검색하고 실행합니다.

Ctrl+ 를 눌러 두 번째 프로그램을 중단 할 수 있습니다 C. 무엇을 해야 일어날 것은 그 두 번째 프로그램은 정지하고, 배쉬 스크립트는 정리를 실행하고 있습니다. 무엇 실제로 일어나는 것은 기본 응용 프로그램 및 배쉬 스크립트가 모두 종료 할 것입니다. 어떤 문제입니다.

그래서 trap내장을 사용하여 Bash에게 SIGINT를 무시하도록 지시했습니다. 이제 Ctrl+ C는 C ++ 응용 프로그램을 종료하지만 Bash는 계속 실행됩니다. 큰.

오 예. 때때로 "두 번째 응용 프로그램"은 또 다른 Bash 스크립트입니다. 그리고에 경우, Ctrl+ C지금 않습니다 아무것도 무엇이든지를 .

분명히이 물건의 작동 방식에 대한 나의 이해가 잘못되었습니다 ... 사용자가 Ctrl+를 누를 때 어떤 프로세스가 SIGINT를 얻는 지 어떻게 제어 C합니까? 이 신호를 하나의 특정 프로세스로 보내려고 합니다 .

답변:


11

인터넷의 얼굴을 검색하는 많은 시간 후에, 나는 그 답을 찾았습니다.

  1. 리눅스는 프로세스 그룹 이라는 개념을 가지고 있다 .

  2. TTY 드라이버에는 Foreground Process Group이라는 개념이 있습니다.

  3. Ctrl+ 를 누르면 CTTY 가 Foreground Process Group의 모든 프로세스 로 전송 SIGINT합니다 . ( 이 블로그 항목 도 참조하십시오 .)

그렇기 때문에 컴파일 된 바이너리 이를 시작하는 스크립트가 모두 혼란스러워집니다. 사실 나는 주 응용 프로그램이 시작 스크립트가 아닌 이 신호를 수신하기를 원합니다 .

해결책은 분명합니다. 응용 프로그램을 새로운 프로세스 그룹에 배치하고이 TTY를위한 Foreground Process Group으로 만들어야합니다. 분명히 그렇게하는 명령은

setsid -c <applcation>

그리고 그게 다야. 이제 사용자가 Ctrl+를 누르면 CSIGINT가 응용 프로그램 (및 자식이있을 수있는 자식)으로 보내지고 다른 사람은 보내지 않습니다. 내가 원하는 것입니다.

  • setsid 그 자체로 애플리케이션을 새로운 프로세스 그룹 (실제로 프로세스 그룹의 그룹 인 완전히 새로운 "세션")에 넣습니다.

  • -c플래그를 추가 하면이 새 프로세스 그룹이 현재 TTY의 "전경"프로세스 그룹이됩니다. (즉, + SIGINT를 누르면 나타납니다 )CtrlC

Bash가 새로운 프로세스 그룹에서 프로세스를 실행하거나 실행하지 않을 때에 대해 많은 상충되는 정보를 보았습니다. (특히 "대화식"과 "비 대화식"셸에서는 다른 것으로 보입니다.) 나는 이것이 당신이 영리한 파이프 속임수 로 작동하게 할 수 있다는 제안을 보았습니다 ... 모릅니다. 그러나 위의 접근 방식은 저에게 효과적입니다.


5
스크립트를 실행할 때 기본적으로 작업 제어가 비활성화되어 있지만이를 사용하여 활성화 할 수 있습니다 set -m. setsid아이 를 뛸 때마다 사용 하는 것보다 조금 더 깨끗하고 간단 합니다.
psusi

@psusi 팁 주셔서 감사합니다! 아이를 한 명만 데려 가면 별 문제가되지 않습니다. 내가 지금 어디에 배쉬 매뉴얼하지만 보면하는 방법을 알고 ...
MathematicalOrchid

아이러니하게도, 나는 부모가 sigint를 잡기를 원한다면 반대의 문제가 있지만, "영리한 파이프 속임수"때문이 아닙니다. 또한 진행 그룹-> 프로세스 그룹
Andrew Domaszek

2

f01에 대한 의견에서 언급했듯이 SIGTERM을 자식 프로세스로 보내야합니다. ^ C를 포착하고 자식 프로세스에 신호를 보내는 방법을 보여주는 두 개의 스크립트가 있습니다.

먼저 부모님.

트랩 테스트

#!/bin/bash

# trap test
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
child=sleeploop

set_trap()
{
    sig=$1
    msg="echo -e \"\n$myname received ^C, sending $sig to $child, $pid\""
    trap "$msg; kill -s $sig $pid" SIGINT
}
trap "echo \"bye from $myname\"" EXIT

echo "running $child..."
./$child 5  &
pid=$!

# set_trap SIGINT
set_trap SIGTERM
echo "$child pid = $pid"

wait $pid
echo "$myname finished waiting"

그리고 지금, 아이.

슬립 루프

#!/bin/bash

# child script for traptest
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
delay="$1"

set_trap()
{
    sig=$1
    trap "echo -e '\n$myname received $sig signal';exit 0" $sig
}

trap "echo \"bye from $myname\"" EXIT
set_trap SIGTERM
set_trap SIGINT

#Select sleep mode
if false
then
    echo "Using foreground sleep"
    Sleep()
    {
        sleep $delay
    }
else
    echo "Using background sleep"
    Sleep()
    {
        sleep "$delay" &
        wait $!
    }
fi

#Time to snooze :)
for ((i=0; i<5; i++));
do
    echo "$i: sleeping for $delay"
    Sleep
done

echo "$myname terminated normally"

traptest가 SIGTERM을 보내면 잘 동작하지만 traptest가 SIGINT를 보내면 sleeploop는 그것을 보지 않습니다.

슬립 루프가 SIGTERM을 포착하고 슬립 모드가 포 그라운드이면 현재 슬립에서 깨어날 때까지 신호에 응답 할 수 없습니다. 그러나 수면 모드가 백그라운드이면 즉시 응답합니다.


이 위대한 예를 들어 주셔서 감사합니다, 내가 내 스크립트 : 개선 할 수있는 방법을 이해하고 나에게 많은 도움
TabeaKischka

2

시작 bash 스크립트에서.

  • 두 번째 프로그램의 PID를 추적

  • SIGINT를 잡아

  • SIGINT를 잡으면 두 번째 프로그램 PID에 SIGINT를 보냅니다.


1
도움이되지 않을 수도 있습니다. 부모 스크립트에서 SIGINT를 트랩하면 부모 또는 다른 셸에서 SIGINT를 보낼 때 자식 스크립트가 SIGINT를받을 수 없습니다. 어쨌든 SIGTERM을 자식에게 보낼 수 있기 때문에 그것은 나쁘지 않습니다.
PM 2Ring

1
SIGTERM이 "바람직한"이유는 무엇입니까?
MathematicalOrchid

그들은 거의 비슷합니다. SIGINT는 제어 터미널 / 사용자가 보낸 신호입니다 (예 : Ctrl + C). SIGTERM은 프로세스를 종료하려는 경우 보낼 수도 있습니다. 더 많은 생각을 여기에 en.wikipedia.org/wiki/Unix_signal
f01
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.