데몬을 만들 때 이중 포크를 수행하는 이유는 무엇입니까?


165

파이썬에서 데몬을 만들려고합니다. 다음 질문을 찾았습니다. 현재 질문에 대한 좋은 자료가 있지만 이중 포크가 필요한 이유가 궁금합니다. 나는 구글을 긁었고 하나는 필요하다고 선언하는 많은 리소스를 찾았지만 그 이유는 아닙니다.

어떤 사람들은 데몬이 제어 터미널을 얻는 것을 막는 것이라고 언급합니다. 두 번째 포크가 없으면 어떻게할까요? 영향은 무엇입니까?



2
이중 포크를 수행하는 데있어 한 가지 어려움은 부모가 손자 프로세스의 PID를 쉽게 얻을 수 없다는 것입니다 ( fork()호출은 자식의 PID를 부모에게 반환하므로 자식 프로세스의 PID를 얻는 것은 쉽지만 쉽지는 않습니다) 손자 프로세스 의 PID를 얻습니다 ).
Craig McQueen

답변:


105

질문에서 참조 된 코드를 보면 정당성은 다음과 같습니다.

좀비를 방지하기 위해 두 번째 아이를 포크하고 즉시 나가십시오. 이로 인해 두 번째 하위 프로세스가 분리되어 초기화 프로세스가 정리를 담당하게됩니다. 그리고 첫 번째 자식은 제어 터미널이없는 세션 리더이므로 나중에 터미널을 열어서이를 획득 할 수 있습니다 (System V 기반 시스템). 이 두 번째 포크는 자식이 더 이상 세션 리더가 아니 어서 데몬이 제어 터미널을 획득하지 못하게합니다.

따라서 데몬이 init로 다시 보호되도록 (데몬을 시작하는 프로세스가 오래 지속되는 경우를 대비하여) 데몬이 제어 tty를 다시 획득 할 가능성을 제거합니다. 따라서이 두 경우 중 어느 것도 해당되지 않으면 하나의 포크이면 충분합니다. " 유닉스 네트워크 프로그래밍-Stevens "에는 이것에 대한 좋은 섹션이 있습니다.


28
이것은 완전히 정확하지 않습니다. 데몬을 만드는 표준 방법은 간단하게하는 것 p=fork(); if(p) exit(); setsid()입니다. 이 경우, 상위도 종료되고 첫 번째 하위 프로세스가 다시 작성됩니다. 더블 포크 마술은 데몬이 tty를 얻지 못하도록하기 위해서만 필요합니다.
parasietje

1
나는 그것을 이해 그래서, 내 프로그램이 시작하면 과정이 매우 첫 번째 자식 프로세스가 될 것입니다 및 TTY 터미널을 열 수 있습니다. 그러나이 아이에게서 다시 포크하고 첫 아이를 종료하면 두 번째 아이는 TTY 터미널을 열 수 없습니다. 이 진술이 맞습니까? forkschildsession leadersession leader
tonix 2016 년

2
@tonix : 단순히 포크하면 세션 리더가 만들어지지 않습니다. 에 의해 수행됩니다 setsid(). 따라서 첫 번째 분기 프로세스는 호출 후 세션 리더가되고 setsid()다시 이중 분기하여 최종 이중 분기 프로세스는 더 이상 세션 리더가되지 않습니다. setsid()세션 리더가되기위한 요구 외에는 당신이 자리를 잡고 있습니다.
dbmikus

169

나는 이중 포크를 이해하려고 노력했고 여기 에서이 질문을 우연히 발견했습니다. 많은 연구 끝에 이것이 내가 알아 낸 것입니다. 다행스럽게도 같은 질문을 가진 사람에게는 더 잘 설명 할 수 있기를 바랍니다.

유닉스에서 모든 프로세스는 그룹에 속하고 세션에 속합니다. 계층 구조는 다음과 같습니다.

세션 (SID) → 프로세스 그룹 (PGID) → 프로세스 (PID)

프로세스 그룹의 첫 번째 프로세스는 프로세스 그룹 리더가되고 세션의 첫 번째 프로세스는 세션 리더가됩니다. 모든 세션에는 하나의 TTY가 연결될 수 있습니다. 세션 리더 만 TTY를 제어 할 수 있습니다. 프로세스가 실제로 디먼 처리 (배경에서 실행)되도록하려면 세션 리더가 종료되어 세션이 TTY를 제어 할 가능성이 없도록해야합니다.

우분투 의이 사이트 에서 Sander Marechal의 Python 예제 데몬 프로그램을 실행 했습니다 . 내 의견과 결과는 다음과 같습니다.

1. `Parent`    = PID: 28084, PGID: 28084, SID: 28046
2. `Fork#1`    = PID: 28085, PGID: 28084, SID: 28046
3. `Decouple#1`= PID: 28085, PGID: 28085, SID: 28085
4. `Fork#2`    = PID: 28086, PGID: 28085, SID: 28085

주 과정 후 세션 리더임을 Decouple#1이 때문에, PID = SID. 여전히 TTY를 제어 할 수 있습니다.

참고 Fork#2더 이상 세션 리더입니다 PID != SID. 이 프로세스는 TTY를 제어 할 수 없습니다. 정말 데몬입니다.

나는 개인적으로 혼란스러워하는 용어를 두 번이나 포크합니다. 더 좋은 관용구는 포크 분리 포크입니다.

추가 관심 링크 :


두 번 포크하면 부모 프로세스가 더 오랜 시간 동안 실행될 때 좀비가 생성되는 것을 막고 어떤 이유로 든 프로세스가 죽었다는 신호에 대한 기본 처리기를 제거합니다.
Trismegistos

그러나 두 번째는 분리를 호출하고 세션 리더가 된 다음 터미널을 얻을 수 있습니다.
Trismegistos

2
사실이 아닙니다. 첫 번째 fork()는 부모를 닫으면 이미 좀비 생성을 방지합니다.
parasietje

1
위에 인용 된 결과를 생성하는 최소한의 예 : gist.github.com/cannium/7aa58f13c834920bb32c
can.

1
싱글 setsid() 전에 전화하는 것이 fork()좋을까요? 실제로 나는 이 질문에 대한 대답 그 대답이라고 생각합니다 .
Craig McQueen

118

엄밀히 말하면, 이중 포크는 데몬의 부모로 부모의 상관이 없습니다 init. 자녀를 양부모로 만드는 데 필요한 것은 부모가 종료해야한다는 것입니다. 이것은 하나의 포크로만 가능합니다. 또한 이중 포크 자체를 수행해도 데몬 프로세스의 부모가되지 않습니다 init. 데몬의 부모 종료 해야합니다 . 다시 말해, 데몬 프로세스가 부모로 변경되도록 적절한 데몬을 포크 할 때 부모는 항상 종료됩니다 init.

왜 더블 포크? POSIX.1-2008 11.1.3 절, " 제어 터미널 "에 답이 있습니다 (강조 추가).

세션에 대한 제어 단말기 는 구현 정의 방식으로 세션 리더의해 할당된다 . 세션 리더에 제어 터미널이없고 O_NOCTTY옵션 을 사용하지 않고 세션과 아직 연결되지 않은 터미널 장치 파일을 열면 (참조 open()) 터미널이 세션 리더의 제어 터미널이 될지 여부는 구현 정의됩니다. 인 프로세스가있는 경우 가 아닌 세션 리더가 터미널 파일을 열고, 또는 O_NOCTTY옵션이 사용됩니다 open(), 다음 터미널은 호출 프로세스의 제어 터미널이되지 아니한다 .

이것은 데몬 프로세스가 이와 같은 일을하면 ...

int fd = open("/dev/console", O_RDWR);

... 데몬 프로세스가 세션 리더인지 여부 및 시스템 구현에 따라 디먼 프로세스가 제어 터미널로 획득 할 수 있습니다/dev/console . 프로그램은 프로그램이 먼저 세션 리더가 아닌 것을 확인하면 위의 호출이 제어 터미널을 획득하지 않도록 보장 할 수 있습니다 .

일반적으로 데몬을 시작할 때을 setsid호출 한 후 자식 프로세스에서 호출 fork하여 데몬을 제어 터미널에서 분리합니다. 그러나 호출 setsid은 호출 프로세스가 새 세션의 세션 리더가되므로 데몬이 제어 터미널을 다시 획득 할 가능성을 열어 둡니다. 이중 포크 기술은 디먼 프로세스가 세션 리더가 아닌 것을 보장하며, open위의 예에서와 같이에 대한 호출 이 디먼 프로세스가 제어 터미널을 다시 획득하지 않도록합니다.

이중 포크 기술은 약간 편집증입니다. 당신이 경우는 필요하지 않을 수 있습니다 알고 데몬이 터미널 장치 파일을 열지 않을 것이다. 또한 일부 시스템에서는 데몬이 터미널 장치 파일을 열어도 동작이 구현 정의되어 있지 않아도 필요하지 않을 수 있습니다. 그러나 구현 정의되지 않은 한 가지는 세션 리더 만 제어 터미널을 할당 할 수 있다는 것입니다. 프로세스가 세션 리더가 아닌 경우 제어 터미널을 할당 할 수 없습니다. 따라서 편집 증상을 원하고 데몬 프로세스가 구현 정의 된 특정 사항에 관계없이 실수로 제어 터미널을 얻을 수없는 경우에는 이중 포크 기술이 필수적입니다.


3
+1이 답변은 질문을받은 후 ~ 4 년이 지나서 나빴습니다.
Tim Seguine

12
그러나 여전히 데몬이 제어 터미널을 다시 획득 할 수없는 것이 왜 그렇게 중요한지 설명하지 못합니다.
UloPe

7
키워드는 "무심코"제어 단말기를 취득한다. 프로세스가 터미널을 열면 누군가 터미널에서 ^ C를 발행하는 경우 프로세스를 종료 할 수있는 것보다 프로세스 제어 터미널이됩니다. 따라서 프로세스가 실수로 발생하지 않도록 프로세스를 보호하는 것이 좋습니다. 개인적으로 나는 터미널을 열지 않을 것이라는 것을 알고있는 코드를 위해 단일 포크와 setsid ()를 고수 할 것입니다.
BobDoolittle

1
@BobDoolittle 어떻게 이것이 "무심코"일어날 수 있을까? 프로세스는 단순히 작성하지 않으면 터미널을 여는 것을 의도하지 않을 것입니다. 프로그래머가 코드를 모르고 tty를 열 수 있는지 모른다면 더블 포킹이 유용 할 수 있습니다.
Marius

10
@Marius 데몬의 구성 파일에 다음과 같은 줄을 추가하면 어떻게 될지 상상해보십시오 LogFile=/dev/console. 프로그램이 어떤 파일을 열
Dan Molding

11

나쁜 CTK 에서 가져온 :

"일부 버전의 Unix에서는 데몬 모드로 들어가기 위해 시작시 이중 포크를 수행해야합니다. 이는 단일 분기가 제어 터미널에서 분리되지 않을 수 있기 때문입니다."


3
싱글 포크는 어떻게 제어 터미널에서 분리되지 않고 더블 포크는 분리됩니까? 어떤 유닉스가 발생합니까?
bdonlan

12
데몬은 입력 및 출력 파일 디스크립터 (fd)를 닫아야합니다. 그렇지 않으면 시작된 터미널에 계속 연결됩니다. 분기 된 프로세스는 상위 프로세스를 상속합니다. 분명히 첫 번째 아이는 fds를 닫지 만 모든 것을 정리하지는 않습니다. 두 번째 포크에는 fd가 존재하지 않으므로 두 번째 자식은 더 이상 아무것도 연결할 수 없습니다.
Aaron Digulla

4
@Aaron : 아니오, 데몬 setsid은 초기 포크 후 호출하여 제어 터미널에서 자체적으로 "분리"됩니다 . 그런 다음 다시 포크하고 세션 리더 (호출 한 프로세스 )를 종료 하여 제어 터미널에서 분리 된 상태를 유지setsid 합니다.
Dan Molding

2
@ bdonlan : fork제어 터미널에서 분리되는 것이 아닙니다 . 그게 setsid다야. 그러나 setsid프로세스 그룹 리더가 호출하면 실패합니다. 따라서 프로세스 그룹 리더가 아닌 프로세스에서 호출 되도록 하려면 초기 fork를 수행해야합니다 . 두 번째 는 최종 프로세스 (데몬이 될 프로세스)가 세션 리더가 아닌지 확인합니다. 세션 리더 만 제어 터미널을 얻을 수 있으므로이 두 번째 포크는 데몬이 제어 터미널을 실수로 다시 얻지 않도록합니다. 이것은 모든 POSIX OS에 해당됩니다. setsidsetsidfork
Dan Molding

@DanMoulding 두 번째 자식이 setsid를 호출하고 세션 리더가 된 다음 제어 터미널을 얻을 수 있기 때문에 제어 터미널을 얻지 못한다는 보장은 없습니다.
Trismegistos

7

Stephens와 Rago의 "Unix Environment의 Advanced Programming"에 따르면, 두 번째 포크가 더 권장되며 데몬이 System V 기반 시스템에서 제어 터미널을 얻지 못하도록합니다.


3

한 가지 이유는 부모 프로세스가 자식을 즉시 wait_pid () 한 다음 잊어 버리기 때문입니다. 손자가 죽으면 부모가 초기화되고 기다립니다. 좀비 상태에서 빠져 나옵니다.

결과적으로 상위 프로세스는 분기 된 하위를 인식 할 필요가 없으며 라이브러리 등에서 오래 실행되는 프로세스를 분기 할 수 있습니다.


2

daemon () 호출은 성공하면 상위 호출 _exit ()를 갖습니다. 원래 동기는 아이가 데몬을하는 동안 부모가 추가 작업을 수행하도록하는 것일 수 있습니다.

또한 데몬에 부모 프로세스가없고 init로 reparent하기 위해 필요하다는 잘못된 생각에 기초 할 수도 있습니다. 그러나 이것은 부모가 단일 포크 케이스에서 죽으면 어쨌든 일어날 것입니다.

그래서 나는 결국 모든 것이 전통으로 끝났다고 가정합니다. 어쨌든 부모가 짧은 순서로 죽는 한 싱글 포크이면 충분합니다.



-1

이 방법으로 이해하는 것이 더 쉬울 수 있습니다.

  • 첫 번째 포크와 setsid는 새 세션을 생성합니다 (그러나 프로세스 ID == 세션 ID).
  • 두 번째 포크는 프로세스 ID! = 세션 ID를 확인합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.