왜 `kill -s INT`는 `Ctrl-C`와 다르게 동작합니까?

왜 `kill -s INT`는 `Ctrl-C`와 다르게 동작합니까?

다음으로 시작하세요:

% donothing () { echo $$; sleep 1000000 }
% donothing
47139

이 시점에서 셸을 제어하는 ​​동일한 터미널을 클릭하면 Ctrl-C함수가 donothing종료되고 명령 프롬프트로 돌아갑니다.

하지만 대신 다른 쉘 세션에서 다음을 실행하면

% kill -s INT 47139

...이 donothing기능은아니요종료. ( -47139마지막 인수로 전달하면 위와 동일합니다 kill.)

kill -s INT제어 터미널에서 대화식으로 동일한 효과를 얻으려면 사용 중인 PID와 다른 PID를 사용해야 합니까 ?Ctrl-C

(저는 주로 답변에 관심이 있습니다 zsh.)


편집: Gilles의 의견에 대한 응답: 아니요, 트랩을 설정하지 않았습니다(트랩이 설정되지 않았는지 확인하는 방법을 배우고 싶습니다. 모든 활성 트랩을 나열하는 방법이 있습니까? 예, 재현할 수 있습니다). 이 동작은 zsh -f. 사실, 완전히 "알몸" 효과를 얻기 위해 제가 아는 가장 극단적인 방법으로 이를 재현할 수 있습니다. zsh(더 좋은 방법이 있으면 알려주십시오.)

% /usr/bin/env -i /usr/bin/zsh -f
% donothing () { echo $$; sleep 1000000 }
12345

...등.

원래 게시물 이후 Darwin과 NetBSD에서는 위의 동작을 복제했지만 Linux에서는 복제하지 않았습니다(즉, Linux에서는 위에서 정의한 함수를 kill -s INT <zsh PID>종료 donothing하지만 프로세스의 상위 쉘은 종료하지 않습니다).

아마도 이것은 BSD 스타일일 것입니다. 더 구체적으로 말하면, 제가 시도한 모든 Unix에서 프로세스는 sleepprocess 의 자식이지만 zshLinux에서만 프로세스 SIGINT에 전송된 신호가 프로세스에 전파됩니다.zshsleep

따라서 Darwin과 NetBSD에서 이 함수를 비대화식으로 종료할 수 있으려면 donothing-ing 하위 프로세스의 PID를 찾아서 해당 프로세스로 sleep보내는 방법이 필요합니다 SIGINT(물론 이것은 정말 냉담하게 들립니다). 보다 일반적으로 Darwin 및 NetBSD에서 비대화식으로 쉘 기능을 종료하려면 재귀적으로/깊이 우선 찾기를 수행해야 합니다.자손처리 zsh해서 보내세요 SIGINT...

답변1

Control-c휴면 프로세스를 포함하여 터미널에서 터미널과 관련된 프로세스 그룹으로 인터럽트 신호를 보냅니다. kill을 통해 SIGINT를 보내면 하나의 프로세스에만 도달할 수 있습니다. 이 경우 $$는 휴면 프로세스가 아닌 셸의 프로세스 ID를 반환하므로 잘못된 프로세스가 됩니다. 쉘은 일반적으로 SIGINT를 무시합니다.

답변2

기본적으로 다음을 수행할 수 있습니다.아니요특정 쉘 함수에 신호를 보냅니다. 함수는 절차적 논리 블록을 구성하는 데 편리한 도구입니다. 이는 쉘 스크립트와 유사하지만 동일하지는 않습니다.

쉘 스크립트를 실행하면 현재 실행 중인 쉘/인터프리터가 분기되어 서브쉘/인터프리터가 스크립트를 실행할 수 있습니다. Forking은 완전히 새로운 프로세스를 만드는 것을 의미합니다. 프로세스는 임의의 신호를 보낼 수 있습니다.

쉘 기능을 실행할 때 (암시적) 분기가 없으므로 전용 프로세스가 없으므로 신호를 보낼 수 있는 대상이 없습니다.

터미널을 누르면 Ctrl-C바쁜 프로세스는 셸 자체이며 현재 수행 중인 모든 작업을 중지합니다. 귀하의 예에서는 sleep신호가 수신되었는지(함수가 끝에 도달하여 종료되도록) 또는 함수 실행이 중지되었는지 여부를 말하기조차 어렵습니다 .

$ domuch () { for (( i=0; i<1000000; i++)) {} }

어쨌든 차단될 Ctrl-C것이며 보낼 신호가 전혀 없을 것입니다.

신호를 보내려면 스크립트에 줄을 넣거나 백틱(여기서 표시하는 방법을 모르겠습니다 :-/) 또는 대체 기호를 사용하여 하위 쉘을 포함해야 합니다 $().

$ doless () { $(sleep 1000) }

$!가장 최근에 시작된 백그라운드 프로세스의 PID를 알려주는 특수 인수를 사용할 수 있지만 동일한 셸 내에서만 가능하며 다음을 수행할 수 있습니다.

$ doless &
$ kill -int $!
[1] 30769
[1]  + interrupt  dosome

아직은 신호를 수신하는 기능이 아닙니다 sleep.

흥미롭게도 아래와 같은 함수 내부에서 배경으로 sleep을 전달하면

dotoomuch()  () { $(sleep 1000) & }

Ctrl-Cif 로 터미널을 차단하더라도 이를 중단할 수는 없습니다 . 이것이 원하는 동작인지 확실하지 않습니다(zsh 4.3.10).

관련 정보