ctrl-C에서 신호를 무시하는 프로그램을 강제로 종료할 수 있습니까?

ctrl-C에서 신호를 무시하는 프로그램을 강제로 종료할 수 있습니까?

SIGINT를 무시하지만 포그라운드에서 실행하고 싶은 프로그램이 있습니다. Ctrl-C를 눌러 강제로 닫는 방법을 찾고 싶습니다. ./wrapper.sh my_program잠재적으로 무시된 SIGINT를 감지하고 SIGKILL을 생성하여 오작동하는 프로그램을 강제 종료하는 래퍼( 라고 함)를 작성할 수 있는 방법이 있습니까 ?

이 답변내가 찾고 있는 것과 정반대입니다. 신호를 무시하는 프로그램이 SIGINT에서 종료되도록 강제하고 싶습니다.

답변1

SIGINT를 SIGKILL로 "변환"하는 래퍼를 만들어 보겠습니다.

+ 에 따라 실행 my_program되고 SIGINT 를 얻는 프로세스가 필요합니다 . SIGINT를 받으면 SIGKILL을 에 보내야 합니다 . + 로 인해 실제로 SIGINT 를 받을 필요는 없습니다 . 추가 프로세스로 인해 SIGKILL이 발생하는 것으로 충분합니다.Ctrlcmy_programmy_programCtrlc

관련 사실:

  • 한 프로세스를 다른 프로세스와 함께 실행하는 표준 방법은 프로세스 중 하나를 백그라운드에서 비동기식으로 실행하는 것입니다(예: 종료 &).
  • Ctrl+를 누르면 c포그라운드 프로세스 그룹의 프로세스에 SIGINT를 보내는 것은 터미널 에뮬레이터입니다.
  • 일부 (간단한) 쉘은 동일한 프로세스 그룹에서 모든 것을 실행합니다. 백그라운드에서 명령을 실행하라는 지시를 받으면 /dev/null명령이 입력을 훔치는 것을 방지하기 위해 stdin을 또는 동등한 파일로 리디렉션합니다.
  • 다른 쉘은 별도의 프로세스 그룹에서 각 명령을 실행할 수 있습니다. 백그라운드에서 명령을 실행하라는 지시를 받으면 표준 입력을 그대로 둡니다. 여전히 백그라운드 명령은 제어 터미널에서 입력을 훔칠 수 없습니다.SIGTTIN. 이를 통해 쉘은 터미널에 새로운 전경 프로세스 그룹을 알려 작업을 배경에서 전경으로 이동할 수 있습니다. 이 메커니즘을 작업 제어라고 하며 비활성화할 수 있습니다. 스크립트에서는 기본적으로 비활성화되어 있습니다.
  • 어느 쪽이든 백그라운드 프로세스는 터미널에서 읽을 수 없습니다. 이는 stdin이 터미널이고 터미널 에서 읽어야 하는 my_program경우를 대비하여 백그라운드에서 실행해서는 안 된다는 것을 의미합니다 .my_program
  • 불행하게도 일부 셸에서는 백그라운드에서 다른 프로세스를 실행하면 안 됩니다. 일부 쉘은 작업 제어가 비활성화된 경우에도 별도의 프로세스 그룹을 사용합니다. 포그라운드 프로세스 그룹에 속하지 않는 다른 프로세스는 Ctrl+ 시 SIGINT를 수신하지 않으므로 c이를 SIGKILL로 "변환"할 수 없습니다.
  • 내 Debian 10에는 posh동일한 프로세스 그룹의 모든 것을 실행하는 쉘이 있습니다.

이는 다음 래퍼로 이어집니다.

#!/usr/bin/env posh

( trap 'kill -s KILL 0' INT
  while kill -s 0 "$$" 2>/dev/null; do sleep 1; done
) &
exec "$@"

exec "$@"실행 my_program(또는 사용자가 지정하는 것)이 가능하며 인수도 포함됩니다. 덕분에 exec my_program포장지를 교체해 드립니다. 터미널일 수 있는 stdin에서 읽을 수 있을 뿐만 아니라; 해당 PID는 교체된 래퍼 중 하나가 됩니다. 이런 의미에서 래퍼는 투명합니다. 또한 여기에는 좋은 기능이 있습니다. PID는 시작 my_program전에 알려져 my_program있으며 다른 프로세스(이 경우 백그라운드의 하위 쉘)에서 이를 쉽게 사용하여 my_program실제로 종료되는 시점을 감지할 수 있습니다.

SIGINT trap를 SIGKILL로 "변환"합니다. 참고는 그룹에 있는 경우 kill -s KILL 0하위 항목을 포함하여 전체 프로세스 그룹에 SIGKILL을 보냅니다 my_program(만 종료하려면 대신 my_program사용 kill -s KILL "$$"). 그럼에도 불구하고 오직 kill -s 0 "$$"존재만을 테스트한다 my_program.

동일한 프로세스 그룹에서 모든 것을 실행하는 셸이 필요하지 않은 대안이 있습니다. 비결은 파이프라인의 프로세스가 하나의 프로세스 그룹에서 실행되어야 한다는 것입니다. 영리한 리디렉션을 통해 조각이 서로 연결되지 않는 파이프라인을 구축할 수 있습니다.

#!/bin/sh -

exec 9>&1
( "$@"; kill -s TERM 0 ) >&9 9>&- | (
  trap 'kill -s KILL 0' INT
  while :; do sleep 1; done
)

이 변형에서는 my_program래퍼를 교체하지 않습니다. 두 번째는 killSIGINT를 SIGKILL로 "변환"하는 것입니다. 첫 번째 는 루프가 살아남을 상황에서 종료되는 kill경우 루프를 종료하는 것입니다 .my_program

래퍼를 사용하면 예상대로 작동하지 않을 수 있는 시나리오가 거의 없습니다. 그 중에는:

  • 분기하고 종료 하면 my_program실제 작업을 자식에게 맡깁니다. 문제의 문제가 있는 프로그램은 Ctrl+ c를 시도했기 때문에 이와 같이 동작하지 않을 가능성이 높습니다. 그러나 일반적으로 그럴 수도 있습니다.
  • my_program다른 프로세스 그룹에서 하위 프로세스를 생성하고 해당 상위 프로세스와 함께 해당 프로세스를 종료하려는 경우 .
  • + my_program시 SIGINT를 보내지 않도록 터미널을 구성하는 경우 . 이런 일이 발생한다고 의심되면 및 사이에 넣어 래퍼를 개선하세요 . 이 경우 프로그램은 SIGINT를 무시하지 않을 수도 있으며 단순히 터미널에서 SIGINT를 가져오지 않도록 할 수도 있습니다. 따라서 SIGKILL은 과잉일 수도 있습니다. 아마도 터미널의 이 기능을 복원하는 것만으로도 충분할 것이며 +가 작동하기 시작할 것입니다.Ctrlcstty -F /dev/tty intr ^Csleep 1doneCtrlc

댓글에서:

SIGINT를 무시하는 프로그램은 일반적으로 아주 좋은 이유로 이를 수행합니다.

진실. SIGKILL 대신 SIGTERM 또는 SIGHUP을 사용해 보세요. 어쩌면 문제의 프로그램이 이들 중 적어도 하나를 무시하지 않고 적절한 정리를 통해 정상적으로 종료될 수도 있습니다.

관련 정보