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

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

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

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

답변1

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

my_program+에서 SIGINT를 실행하고 가져오는 프로세스가 필요합니다 . SIGINT를 받으면 SIGKILL을 보내야 합니다. 추가 프로세스로 인해 SIGKILL로 인해 발생한 SIGINT를 실제로 수신할 필요는 없습니다.Ctrlcmy_programmy_programCtrlc

관련 사실:

  • 한 프로세스를 다른 프로세스와 함께 실행하는 표준 방법은 프로세스 중 하나를 백그라운드에서 비동기식으로 실행하는 것입니다(즉, 종료 &).
  • Ctrl+ 를 클릭하면 c터미널 에뮬레이터는 포그라운드 프로세스 그룹의 프로세스에 SIGINT를 보냅니다.
  • 일부 (간단한) 쉘은 동일한 프로세스 그룹에서 모든 것을 실행합니다. 백그라운드에서 명령을 실행하라는 지시를 받으면 /dev/null명령이 입력을 훔치는 것을 방지하기 위해 표준 입력을 동등한 파일로 리디렉션합니다.
  • 다른 쉘은 별도의 프로세스 그룹에서 각 명령을 실행할 수 있습니다. 백그라운드에서 명령을 실행하라는 지시를 받으면 표준 입력을 변경하지 않고 그대로 둡니다. 백그라운드의 명령은 여전히 ​​제어 터미널에서 입력을 훔칠 수 없습니다.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. 포장지를 교체해 드리겠습니다. 표준 입력(아마도 터미널)에서 데이터를 읽을 수 있을 뿐만 아니라 해당 PID는 대체되는 래퍼 중 하나가 됩니다. 이런 의미에서 래퍼는 투명합니다. 또한 PID는 시작하기 my_program전에 알려져 my_program있으며 다른 프로세스(이 경우 백그라운드의 하위 쉘)에서 쉽게 PID를 사용하여 my_program실제로 종료된 시기를 감지할 수 있다는 좋은 기능도 있습니다.

" 변환 trap" SIGINT를 SIGKILL로 변환합니다. 하위 프로세스가 그룹에 있는 경우 이를 kill -s KILL 0포함하여 전체 프로세스 그룹에 SIGKILL을 보내는 것을 참고하세요 ( 단지 종료하려는 경우 대신 이것을 사용하십시오) . 그러나 그 존재 여부는 테스트만 가능합니다 .my_programmy_programkill -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하위 프로세스가 다른 프로세스 그룹에서 생성되고 해당 상위 프로세스와 함께 해당 프로세스를 종료하려는 경우 .
  • 터미널이 +에서 SIGINT를 보내지 my_program않도록 구성된 경우 . 이런 일이 발생한다고 의심되면 및 사이에 배치하여 래퍼를 개선하세요. 이 경우 프로그램은 SIGINT를 무시하지도 않고 터미널에서 SIGINT를 가져오지 않도록 할 수도 있습니다. 따라서 SIGKILL은 약간 과잉일 수 있습니다. 이 기능을 터미널에 복원하는 것만으로도 충분하고 +가 작동하기 시작할 것입니다.Ctrlcstty -F /dev/tty intr ^Csleep 1doneCtrlc

댓글에서:

SIGINT를 무시하는 프로그램에는 대개 그럴 만한 이유가 있습니다.

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

관련 정보