Bash 5.1에서 백그라운드 작업을 생성하고 즉시 신호를 보내면 신호가 손실된 것 같습니다. 짧은 데모:
$ cat simple.cc
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
static void handler(int, siginfo_t *, void *)
{
write(2, "Got SIGINT\n", 11);
exit(0);
}
int main()
{
struct sigaction act;
act.sa_flags = SA_SIGINFO | SA_RESTART;
act.sa_sigaction = handler;
if (sigaction(SIGINT, &act, nullptr)) { exit(1); }
while (true) {}
}
$ g++ -Wall -Wextra simple.cc -o simple
$ ./simple & kill -SIGINT $!
[1] 540434
$ # nothing happens
$
$ kill -SIGINT 540434
Got SIGINT
내 가정은 신호가 도달할 때 포크된 백그라운드 프로세스가 여전히 Bash를 실행하고 있다는 것입니다. Bash는 SIGINT를 포그라운드 프로세스로 전달하려고 시도하지만 안타깝게도 그렇지 않아 SIGINT가 삭제됩니다.
내 질문:
- 맞습니까? 실제로 이런 일이 일어나고 있는지 어떻게 확인할 수 있나요?
- 백그라운드에서 실행되도록 프로그램을 수정할 수 없다고 가정해 보겠습니다. 이미 활성화되어 있고 신호를 처리할 것인지 어떻게 확인할 수 있나요? 간단한 접근 방식이
sleep 1
도움이 되지만 올바른 동기화를 찾고 있습니다. 예상되는 신호 처리기가 설정되어 있는지 여부는 신경 쓰지 않으며 올바른 바이너리가 실행되는지만 확인하십시오.
답변1
참고: 이 답변에는 가 필요합니다 /proc
.
[...] SIGINT는 폐기됩니다. [...] 맞나요?
기본적으로 그렇습니다. 귀하의 설명이 정확한지는 잘 모르겠습니다("SIGINT 전달"?). 메인(예: 대화형) bash
이 분기되고 라인 실행이 초과되면 &
PID가 있는 프로세스가 있고 $!
신호가 도달합니다. 문제는 이 과정이 초기에또 하나 bash
가 exec
온다./simple
.확실히상대방이 bash
신호를 받았습니다. 그러나 종료하는 대신 으로 대체되지만 ./simple
신호가 "사용"되었습니다.
나는 프로그래머도 아니고 *nix 전문가도 아닙니다. 내 설명이 정확한지 잘 모르겠습니다. 완전히 정확하지는 않더라도 이 답변의 나머지 부분이 유용할 수 있습니다.
실제로 이런 일이 일어나고 있는지 어떻게 확인할 수 있나요?
문제가 발생하는 기간은 매우 좁습니다. 종종 kill
문제의 "해결"을 미루는 것만으로도 충분합니다. 이론적으로는 아무리 큰 지연이라도 특정 시도에서는 충분히 크지 않을 수 있습니다. "적절한 동기화"에 관해 글을 쓰시면 이 점을 이해하신 것 같습니다.
kill
문제를 유발할 만큼 빠른 Bash의 내장 기능입니다. 외부 kill
(예:)를 사용할 수 있습니다 . /bin/kill
이는 시간이 걸리는 별도의 프로세스로 생성되었으며 테스트에서는 문제가 발생하지 않았습니다.
bash
다른 것이 ; 로 대체되기 ./simple
전에 잡으려고 합니다 . 나는 확인하려고 노력했다 /proc/$!/exe
. 불행히도 너무 느리 ls -l
거나 readlink
내장되어 있지 않습니다.
유용한 내장 함수는 test
또는 동의어 입니다 [
. 내 bash
것은 /bin/bash
다음과 같이 할 수 있습니다.
./simple & [ "/proc/$!/exe" -ef /bin/bash ] && echo gotcha
(테스트할 때 이 작업을 수행하는 것을 잊지 마십시오 killall simple
.) 이것이 수행하는 작업을 help test
보려면 Bash를 참조하십시오.-ef
bash
다음으로 전환하기 전에 수행할 수 있는 테스트 수를 살펴보겠습니다 simple
.
./simple & while [ "/proc/$!/exe" -ef /bin/bash ]; do echo a; done
제가 테스트한 결과 에코 문자열의 개수는 약 5개였습니다. 처음에는 파이프를 통해 개수를 계산하려고 했지만 wc -l
결과적으로 지연이 발생했습니다 0
.
백그라운드 프로세스가 아직 실행되지 않은 동안 기본 코드가 계속 실행되는 기간은 ./simple
실제로 좁지만 존재합니다.
백그라운드에서 실행되도록 프로그램을 수정할 수 없다고 가정해 보겠습니다. 이미 활성화되어 있고 신호를 처리할 것인지 어떻게 확인할 수 있나요? [...] 예상되는 신호 처리기가 설정되었는지 여부는 신경 쓰지 않고 올바른 바이너리가 실행되는지만 확인하십시오.
/proc/$!/exe
루프 테스트. bash
가 된 후 루프를 종료합니다 simple
. 테스트가 매우 빠를 필요는 없습니다. 다음 사항에 유의하세요.
while [ "/proc/$!/exe" -ef /bin/bash ]; do :; done
하드코드/bin/bash
.until [ "/proc/$!/exe" -ef ./simple ]; do :; done
simple
충분히 빠르게 종료되거나 실행되지 않는 경우(예: 일부 오류로 인해) 무한 루프가 발생합니다.
나는 이것이 합리적인 접근 방식이라고 생각합니다.
./simple & while [ "/proc/$!/exe" -ef "/proc/$$/exe" ]; do :; done; kill -s INT "$!"
내 테스트에서는 핸들러가 설정되기 전에는 신호가 simple
도착하지 않았으며 나중에 메인 셸에서 알림을 보낼 때마다 작업이 중단되었습니다. "예상되는 신호 처리기가 설정되어 있는지 여부는 중요하지 않으며 올바른 바이너리가 실행되는 것만 중요합니다."라고 썼습니다. 예, 그게 당신이 할 수 있는 일입니다.