정확히 하나의 자식 프로세스를 생성하는 프로세스가 있다고 가정하십시오. 이제 부모 프로세스가 어떤 이유로 든 (일반적으로 또는 비정상적으로 kill, ^ C, assert failure 등) 종료 될 때 자식 프로세스가 죽기를 원합니다. 올바르게하는 방법?
stackoverflow에 대한 비슷한 질문 :
Windows의 stackoverflow에 대한 비슷한 질문 :
정확히 하나의 자식 프로세스를 생성하는 프로세스가 있다고 가정하십시오. 이제 부모 프로세스가 어떤 이유로 든 (일반적으로 또는 비정상적으로 kill, ^ C, assert failure 등) 종료 될 때 자식 프로세스가 죽기를 원합니다. 올바르게하는 방법?
stackoverflow에 대한 비슷한 질문 :
Windows의 stackoverflow에 대한 비슷한 질문 :
답변:
아이가 제공하는 커널을 요청할 수 있습니다 SIGHUP
(또는 다른 신호) 때 옵션을 지정하여 부모 다이 PR_SET_PDEATHSIG
에 prctl()
이 같은 콜 :
prctl(PR_SET_PDEATHSIG, SIGHUP);
보다 man 2 prctl
을 참조하십시오.
편집 : 이것은 Linux 전용입니다
prctl()
. Btw, Maxim의 답변이 잘못되었습니다.
man prctl
: 호출 프로세스의 상위 프로세스 종료 신호를 arg2 (1..maxsig 범위의 신호 값 또는 지우려면 0)로 설정하십시오. 이것은 부모가 죽을 때 호출 프로세스가 얻을 것이라는 신호입니다. set-user-ID 또는 set-group-ID 바이너리를 실행할 때 fork (2) 및 (Linux 2.4.36 / 2.6.23부터) 자식에 대해이 값이 지워집니다.
같은 문제를 해결하려고하는데 프로그램이 OS X에서 실행되어야하기 때문에 Linux 전용 솔루션이 작동하지 않았습니다.
이 페이지의 다른 사람들과 같은 결론에 도달했습니다. 부모가 죽을 때 아이에게 알리는 POSIX 호환 방법은 없습니다. 그래서 나는 다음으로 가장 좋은 일을 멈췄습니다.
어떤 이유로 든 부모 프로세스가 종료되면 (어떤 이유로 든) 자식의 부모 프로세스가 프로세스 1이됩니다. 자식이 단순히 정기적으로 폴링하는 경우 부모가 1인지 여부를 확인할 수 있습니다.
이것은 좋지는 않지만 작동 하며이 페이지의 다른 곳에서 제안 된 TCP 소켓 / 잠금 파일 폴링 솔루션보다 쉽습니다.
gettpid()
1이되지 않지만 pid
영역 스케줄러 (process zsched
)를 가져옵니다 .
과거에는 "자식"에서 "원본"코드를 실행하고 "부모"에서 "스폰 된"코드를 실행하여이 작업을 수행했습니다. fork()
)이를 달성했습니다. 그런 다음 "spawned"코드에서 SIGCHLD를 트랩하십시오.
귀하의 경우에는 가능하지 않지만 작동하면 귀엽습니다.
자식 프로세스를 수정할 수없는 경우 다음과 같은 방법을 시도해보십시오.
int pipes[2];
pipe(pipes)
if (fork() == 0) {
close(pipes[1]); /* Close the writer end in the child*/
dup2(0, pipes[0]); /* Use reader end as stdin */
exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}
close(pipes[0]); /* Close the reader end in the parent */
작업 제어가 사용 가능한 쉘 프로세스에서 하위를 실행합니다. 자식 프로세스는 백그라운드에서 생성됩니다. 쉘은 개행 (또는 EOF)을 기다린 다음 아이를 죽입니다.
이유가 무엇이든 부모가 죽으면 파이프 끝이 닫힙니다. 자식 셸은 읽기에서 EOF를 가져오고 백그라운드 자식 프로세스를 종료합니다.
dup2
사용하여 read -u
특정 파일 디스크립터에서 읽음 으로써 및 stdin 인수를 피할 수 있습니다 . 또한 setpgid(0, 0)
터미널에서 ^ C를 누를 때 종료되지 않도록 자식을 추가했습니다 .
dup2()
호출 의 인수 순서 가 잘못되었습니다. pipes[0]
stdin 으로 사용하려면 dup2(pipes[0], 0)
대신에 작성해야합니다 dup2(0, pipes[0])
. 그것은이다 dup2(oldfd, newfd)
호출이 이전에 열린 newfd를 닫 곳.
Linux에서는 다음과 같이 자녀에게 부모 사망 신호를 설치할 수 있습니다.
#include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
#include <signal.h> // signals
#include <unistd.h> // fork()
#include <stdio.h> // perror()
// ...
pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() != ppid_before_fork)
exit(1);
// continue child execution ...
분기 전에 상위 프로세스 ID를 저장하고 하위에서 테스트하면 하위 를 호출 한 프로세스와 종료 prctl()
사이의 경쟁 조건이 제거 prctl()
됩니다.
또한 아동의 부모 사망 신호는 새로 생성 된 아동 자체에서 지워집니다. 의 영향을받지 않습니다 execve()
.
모든 고아 입양을 담당하는 시스템 프로세스에 PID 1 이 있는지 확인하면이 테스트를 단순화 할 수 있습니다 .
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() == 1)
exit(1);
// continue child execution ...
그러나 시스템 프로세스에 의존하고 init
PID 1을 갖는 것은 이식성이 없습니다. POSIX.1-2008은 다음을 지정합니다 .
기존의 모든 자식 프로세스와 호출 프로세스의 좀비 프로세스의 부모 프로세스 ID는 구현 정의 시스템 프로세스의 프로세스 ID로 설정해야합니다. 즉, 이러한 프로세스는 특수 시스템 프로세스에 의해 상속됩니다.
전통적으로 모든 고아를 채택하는 시스템 프로세스는 PID 1, 즉 init-모든 프로세스의 조상입니다.
Linux 또는 FreeBSD 와 같은 최신 시스템에서는 다른 프로세스가 그 역할을 수행 할 수 있습니다. 예를 들어, Linux에서는 프로세스가 prctl(PR_SET_CHILD_SUBREAPER, 1)
자신의 자손 중 모든 고아를 상속하는 시스템 프로세스로 설정하기 위해 호출 할 수 있습니다 (참조, Fedora 25 의 예 ).
init(8)
.... 부모 프로세스가 죽을 때 부모 ID가 변경된다는 것만 가정 할 수 있습니다. 이것은 실제로 프로세스 수명 동안 한 번만 발생합니다. 프로세스의 부모가 죽을 때입니다. 여기에는 단 하나의 주요 예외가 있으며 init(8)
어린이 에게는 예외입니다 . 그러나 init(8)
결코 exit(2)
그런 경우 로부터 보호받습니다 (이 경우 커널 패닉)
완전성을 위해. macOS에서는 kqueue를 사용할 수 있습니다.
void noteProcDeath(
CFFileDescriptorRef fdref,
CFOptionFlags callBackTypes,
void* info)
{
// LOG_DEBUG(@"noteProcDeath... ");
struct kevent kev;
int fd = CFFileDescriptorGetNativeDescriptor(fdref);
kevent(fd, NULL, 0, &kev, 1, NULL);
// take action on death of process here
unsigned int dead_pid = (unsigned int)kev.ident;
CFFileDescriptorInvalidate(fdref);
CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example
int our_pid = getpid();
// when our parent dies we die as well..
LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
exit(EXIT_SUCCESS);
}
void suicide_if_we_become_a_zombie(int parent_pid) {
// int parent_pid = getppid();
// int our_pid = getpid();
// LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);
int fd = kqueue();
struct kevent kev;
EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
kevent(fd, &kev, 1, NULL, 0, NULL);
CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
NSTask
했거나 posix 스폰. startTask
여기 내 코드에서 함수를 참조 하십시오 : github.com/neoneye/newton-commander-browse/blob/master/Classes/…
여기에 또 다른 대답에서 영감을 얻은 다음과 같은 모든 POSIX 솔루션을 생각해 냈습니다. 일반적인 아이디어는 부모와 자식 사이에 중간 프로세스를 생성하는 것입니다. 부모 프로세스는 부모가 죽을 때를 통지하고 자식을 명시 적으로 죽이는 것입니다.
이 유형의 솔루션은 자식의 코드를 수정할 수 없을 때 유용합니다.
int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
close(p[1]); // close write end of pipe
setpgid(0, 0); // prevent ^C in parent from stopping this process
child = fork();
if (child == 0) {
close(p[0]); // close read end of pipe (don't need it here)
exec(...child process here...);
exit(1);
}
read(p[0], 1); // returns when parent exits for any reason
kill(child, 9);
exit(1);
}
이 방법에는 두 가지 작은 경고가 있습니다.
옆으로, 내가 사용하는 실제 코드는 Python입니다. 완전성을위한 것입니다 :
def run(*args):
(r, w) = os.pipe()
child = os.fork()
if child == 0:
os.close(w)
os.setpgid(0, 0)
child = os.fork()
if child == 0:
os.close(r)
os.execl(args[0], *args)
os._exit(1)
os.read(r, 1)
os.kill(child, 9)
os._exit(1)
os.close(r)
표준 POSIX 호출 만 사용한다고 보장 할 수는 없습니다. 실제 생활과 마찬가지로, 일단 자녀가 태어나면 그 자체의 삶을 갖게됩니다.
그것은 이다 부모 프로세스가 최대한 종료 이벤트를 캐치하고, 그 시점에서 자식 프로세스를 종료하려고 시도 할 수 있지만, 항상 잡힐 수없는 몇 가지가있다.
예를 들어 어떤 프로세스도 SIGKILL
. 커널이이 신호를 처리하면 해당 프로세스에 대한 알림없이 지정된 프로세스를 종료합니다.
유추를 확장하려면-다른 표준 방법은 부모가 더 이상 없다는 것을 알게되면 자살하는 것입니다.
Linux 전용 방법이 있습니다 prctl(2)
-다른 답변을 참조하십시오.
다른 사람들이 지적했듯이, 부모 출구가 이동할 수 없을 때 부모 pid에 의존하여 1이됩니다. 특정 상위 프로세스 ID를 기다리는 대신 ID가 변경 될 때까지 기다리십시오.
pit_t pid = getpid();
switch (fork())
{
case -1:
{
abort(); /* or whatever... */
}
default:
{
/* parent */
exit(0);
}
case 0:
{
/* child */
/* ... */
}
}
/* Wait for parent to exit */
while (getppid() != pid)
;
최고 속도로 폴링하지 않으려면 원하는대로 마이크로 슬립을 추가하십시오.
이 옵션은 파이프를 사용하거나 신호에 의존하는 것보다 간단합니다.
getpid()
는 호출하기 전에 부모에서 수행됩니다 fork()
. 자녀가 존재하기 전에 부모가 사망하는 경우. 일어날 수있는 일은 아이가 부모를 잠시 살면서 나가는 것입니다.
이 솔루션은 저에게 효과적이었습니다.
이것은 부모가 살아있을 때만 존재하는 작업자 유형 프로세스를위한 것입니다.
일부 포스터는 이미 파이프와을 언급했습니다 kqueue
. 사실 당신은 또한 연결된 한 쌍의 생성 할 수 있습니다 유닉스 도메인 소켓 바이 socketpair()
전화를. 소켓 유형은이어야합니다 SOCK_STREAM
.
두 개의 소켓 파일 디스크립터 fd1, fd2가 있다고 가정 해 봅시다. 이제 fork()
자식 프로세스를 생성하여 fd를 상속합니다. 부모에서 fd2를 닫고 자식에서 fd1을 닫습니다. 이제 각 프로세스는 이벤트를 poll()
위해 남은 열린 fd를 자체적으로 종료 할 수 있습니다 POLLIN
. close()
정상적인 수명 동안 각면이 fd를 명시 적으로 나타내지 않는 한 POLLHUP
플래그는 상대방의 종료를 표시해야합니다 (깨끗한 지 여부에 상관없이). 이 사건을 통보받은 어린이는 무엇을해야할지 결정할 수 있습니다 (예 : 사망).
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
int sv[2]; /* sv[0] for parent, sv[1] for child */
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
pid_t pid = fork();
if ( pid > 0 ) { /* parent */
close(sv[1]);
fprintf(stderr, "parent: pid = %d\n", getpid());
sleep(100);
exit(0);
} else { /* child */
close(sv[0]);
fprintf(stderr, "child: pid = %d\n", getpid());
struct pollfd mon;
mon.fd = sv[1];
mon.events = POLLIN;
poll(&mon, 1, -1);
if ( mon.revents & POLLHUP )
fprintf(stderr, "child: parent hung up\n");
exit(0);
}
}
위의 개념 증명 코드를 컴파일하여와 같은 터미널에서 실행할 수 ./a.out &
있습니다. 다양한 신호로 부모 PID를 죽이는 실험을하는 데 약 100 초가 걸리거나 종료됩니다. 두 경우 모두 "child : parent hung up"이라는 메시지가 표시됩니다.
SIGPIPE
핸들러를 사용하는 메소드와 비교 하여이 메소드는 write()
호출을 시도 할 필요가 없습니다 .
이 방법은 대칭 적입니다 . 즉, 프로세스는 동일한 채널을 사용하여 서로의 존재를 모니터링 할 수 있습니다.
이 솔루션은 POSIX 함수 만 호출합니다. 나는 리눅스와 FreeBSD에서 이것을 시도했다. 다른 유닉스에서도 작동해야한다고 생각하지만 실제로 테스트하지는 않았습니다.
또한보십시오:
unix(7)
리눅스 매뉴얼 페이지, unix(4)
FreeBSD를위한, poll(2)
, socketpair(2)
, socket(7)
리눅스에.에서 POSIX 의 exit()
, _exit()
그리고 _Exit()
기능에 정의되어있다 :
따라서 상위 프로세스가 해당 프로세스 그룹에 대한 제어 프로세스가되도록하는 경우 상위 프로세스가 종료 될 때 하위 프로세스는 SIGHUP 신호를 수신해야합니다. 부모가 충돌 할 때 발생하는 것을 확신하지는 않지만 그렇게 생각합니다. 분명히 충돌이 발생하지 않는 경우에는 잘 작동합니다.
에 대한 기본 정의 (정의) 섹션을 포함뿐만 아니라 시스템 서비스 정보 - 당신은 아주 좋은 인쇄를 많이 읽을 필요가 있으므로주의 exit()
하고 setsid()
하고 setpgrp()
- 전체 사진을 얻을 수 있습니다. (나도 그럴거야!)
예를 들어 pid 0에 신호를 보내면
kill(0, 2); /* SIGINT */
그 신호는 전체 프로세스 그룹으로 보내져 효과적으로 자식을 죽입니다.
다음과 같은 방법으로 쉽게 테스트 할 수 있습니다.
(cat && kill 0) | python
그런 다음 ^ D를 누르면 텍스트 "Terminated"
가 표시되어 stdin이 닫혀서 종료 된 대신 파이썬 인터프리터가 실제로 종료되었음을 나타냅니다.
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -"
행복 대신 4 종료의 인쇄
다른 사람과 관련이있는 경우 C ++의 분기 하위 프로세스에서 JVM 인스턴스를 생성 할 때 부모 프로세스가 완료된 후 JVM 인스턴스를 올바르게 종료 할 수있는 유일한 방법은 다음을 수행하는 것입니다. 이것이 최선의 방법이 아닌 경우 누군가 의견에 피드백을 제공 할 수 있기를 바랍니다.
1) 다음을 prctl(PR_SET_PDEATHSIG, SIGHUP)
통해 Java 앱을 시작하기 전에 제안 된 분기 된 자식 프로세스를 호출하십시오 .execv
하고,
2) 상위 PID가 1이 될 때까지 폴링하는 종료 후크를 Java 애플리케이션에 추가 한 후 열심히 수행하십시오 Runtime.getRuntime().halt(0)
. 폴링은 ps
명령 을 실행하는 별도의 쉘을 시작하여 수행됩니다 ( Linux에서 Java 또는 JRuby에서 PID를 찾으려면 어떻게합니까? 참조). ).
편집 130118 :
강력한 솔루션이 아닌 것 같습니다. 나는 아직도 무슨 일이 일어나고 있는지의 뉘앙스를 이해하기 위해 조금 고심하고 있지만 화면 / SSH 세션에서 이러한 응용 프로그램을 실행할 때 여전히 고아 JVM 프로세스를 얻었습니다.
Java 앱에서 PPID를 폴링하는 대신, 종료 후크가 정리를 수행 한 다음 위와 같이 하드 정지를 수행했습니다. 그런 다음 waitpid
모든 것을 종료 할 때가되었을 때 생성 된 자식 프로세스에서 C ++ 부모 앱 을 호출해야했습니다 . 자식 프로세스는 프로세스가 종료되도록 보장하고 부모는 기존 참조를 사용하여 자식이 종료되도록보다 강력한 솔루션 인 것 같습니다. 이것을 부모 프로세스가 원할 때마다 종료하고 종료하기 전에 고아인지 확인하려고 시도했던 이전 솔루션과 비교하십시오.
PID equals 1
대기가 유효하지 않습니다. 새로운 부모는 다른 PID 일 수 있습니다. 원래 부모 (fork () 앞의 getpid ())에서 새 부모 (fork () 전에 호출 될 때 getpid ()와 같지 않은 자식의 getppid ())로 변경되는지 확인해야합니다.
Linux 고유의 다른 방법은 부모를 새로운 PID 네임 스페이스에서 생성하는 것입니다. 그런 다음 해당 네임 스페이스에서 PID 1이되고 종료되면 모든 하위 항목이 즉시 종료됩니다 SIGKILL
.
불행히도, 새로운 PID 네임 스페이스를 만들려면가 있어야 CAP_SYS_ADMIN
합니다. 그러나이 방법은 매우 효과적이며 부모가 처음 시작한 이후에는 부모 나 자녀를 실질적으로 변경할 필요가 없습니다.
참조 클론 (2) , pid_namespaces (7) , 및 공유 해제 (2) .
부모가 사망하면 고아의 PPID가 1로 바뀝니다. 자신의 PPID 만 확인하면됩니다. 어떤 방식으로, 이것은 위에서 언급 한 폴링입니다. 여기에 쉘 조각이 있습니다.
check_parent () {
parent=`ps -f|awk '$2=='$PID'{print $3 }'`
echo "parent:$parent"
let parent=$parent+0
if [[ $parent -eq 1 ]]; then
echo "parent is dead, exiting"
exit;
fi
}
PID=$$
cnt=0
while [[ 1 = 1 ]]; do
check_parent
... something
done
완벽하지 않은 두 가지 솔루션을 찾았습니다.
1. SIGTERM 신호를 받으면 kill (-pid)하여 모든 어린이를 처치하십시오.
분명히이 솔루션은 "kill -9"를 처리 할 수 없지만 모든 하위 프로세스를 기억할 필요가 없기 때문에 대부분의 경우 매우 간단하게 작동합니다.
var childProc = require('child_process').spawn('tail', ['-f', '/dev/null'], {stdio:'ignore'});
var counter=0;
setInterval(function(){
console.log('c '+(++counter));
},1000);
if (process.platform.slice(0,3) != 'win') {
function killMeAndChildren() {
/*
* On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually
* the process itself and children.
* On Windows, an JOB object has been applied to current process and children,
* so all children will be terminated if current process dies by anyway.
*/
console.log('kill process group');
process.kill(-process.pid, 'SIGKILL');
}
/*
* When you use "kill pid_of_this_process", this callback will be called
*/
process.on('SIGTERM', function(err){
console.log('SIGTERM');
killMeAndChildren();
});
}
같은 방법으로 process.exit를 어딘가에 호출하면 위와 같이 'exit'처리기를 설치할 수 있습니다. 참고 : Ctrl + C 및 갑작스러운 충돌은 OS에서 자동으로 처리되어 프로세스 그룹을 종료하므로 더 이상 여기에 없습니다.
2. chjj / pty.js 를 사용하여 제어 터미널이 연결된 상태에서 프로세스를 생성하십시오.
어쨌든 현재 프로세스를 죽일 때조차도 -9를 죽이면 모든 하위 프로세스도 자동으로 종료됩니다 (OS?). 현재 프로세스가 터미널의 다른 쪽을 잡고 있기 때문에 현재 프로세스가 죽으면 자식 프로세스가 SIGPIPE를 얻습니다.
var pty = require('pty.js');
//var term =
pty.spawn('any_child_process', [/*any arguments*/], {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: process.cwd(),
env: process.env
});
/*optionally you can install data handler
term.on('data', function(data) {
process.stdout.write(data);
});
term.write(.....);
*/
터미널 제어 및 세션을 남용하여 3 가지 프로세스로 휴대용 비 폴링 솔루션을 관리했습니다. 이것은 정신 자위이지만 효과가 있습니다.
비결은 다음과 같습니다.
그런 식으로:
단점 :
7 년이 지났지 만 개발 중에 webpack-dev-server를 시작해야하고 백엔드 프로세스가 중지되면 응용 프로그램을 종료 해야하는 SpringBoot 응용 프로그램을 실행 하면서이 문제가 발생했습니다.
사용하려고 Runtime.getRuntime().addShutdownHook
하지만 Windows 10에서는 작동했지만 Windows 7에서는 작동하지 않았습니다.
프로세스가 종료 될 때까지 기다리거나 InterruptedException
두 Windows 버전 모두에서 올바르게 작동하는 것처럼 보이는 전용 스레드를 사용하도록 변경했습니다 .
private void startWebpackDevServer() {
String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
logger.info("webpack dev-server " + cmd);
Thread thread = new Thread(() -> {
ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.directory(new File("."));
Process process = null;
try {
// Start the node process
process = pb.start();
// Wait for the node process to quit (blocking)
process.waitFor();
// Ensure the node process is killed
process.destroyForcibly();
System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
} catch (InterruptedException | IOException e) {
// Ensure the node process is killed.
// InterruptedException is thrown when the main process exit.
logger.info("killing webpack dev-server", e);
if (process != null) {
process.destroyForcibly();
}
}
});
thread.start();
}
역사적으로 UNIX v7부터 프로세스 시스템은 프로세스의 상위 ID를 확인하여 프로세스의 고아를 감지했습니다. 내가 말했듯이 역사적으로 init(8)
시스템 프로세스는 한 가지 이유에 의해서만 특별한 프로세스입니다. 죽을 수 없습니다. 새로운 부모 프로세스 ID 할당을 처리하는 커널 알고리즘은이 사실에 의존하기 때문에 죽을 수 없습니다. 프로세스가 프로세스를 실행 하면 시스템 호출 전에 프로세스가 고아가됩니다.exit(2)
호출을 (프로세스 시스템 호출 또는 신호를 보내는 등의 외부 작업을 통해) 커널은이 프로세스의 모든 자식을 init 프로세스의 id를 상위 프로세스 ID로 다시 할당합니다. 이는 프로세스가 고아인지 여부를 확인하는 가장 쉬운 테스트 및 가장 이식 가능한 방법으로 이어집니다. getppid(2)
시스템 호출 의 결과를 확인하고 시스템 호출의 프로세스 ID인지 확인하십시오.init(2)
이 접근법에서 문제를 일으킬 수있는 두 가지 문제가 있습니다.
init
프로세스를 사용자 프로세스 로 변경할 가능성이 있으므로 init 프로세스가 항상 모든 고아 프로세스의 부모가되도록하려면 어떻게해야합니까? 음,에 exit
시스템 호출 코드 호출을 실행하는 프로세스는 init 프로세스 (PID의 발행 주식 등을 1로 처리) 인 경우 볼 수있는 명시 적 검사가있다 그 사건을의 경우, 커널 패닉은 (는 유지 관리가 더 이상 할 수 없게한다 init 프로세스가 exit(2)
호출 을 수행하는 것이 허용되지 않습니다 .1
되지만 POSIX 접근 방식에서는 보증하지 않지만 다른 응답에 노출 된 것처럼 시스템 프로세스 ID 만 해당 용도로 예약되어 있습니다. posix 구현은 거의 수행하지 않으며 원래 유닉스 파생 시스템 1
에서는 getppid(2)
시스템 호출의 응답으로 프로세스가 고아라고 가정하기에 충분하다고 가정 할 수 있습니다. 확인하는 또 다른 방법 getppid(2)
은 분기 직후에 그 값을 새 호출 결과와 비교하는 것입니다. 두 호출이 모두 원자 적이 지 않고 fork(2)
첫 번째 getppid(2)
시스템 호출 이후 및 이전에 상위 프로세스가 죽을 수 있기 때문에 모든 경우에 단순히 작동하지는 않습니다 . 프로세스 parent id only changes once, when its parent does an
엑시트 (2) call, so this should be enough to check if the
getppid (2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children of
init (8)`이지만 이러한 프로세스는 부모가없는 것으로 안전하게 가정 할 수 있습니다 (시스템에서 init 프로세스를 대체하는 경우 제외)환경을 사용하여 부모 pid를 자식에게 전달한 다음 자식에서 / proc / $ ppid가 있는지 정기적으로 확인했습니다.