nix 환경에서 Daemon형식을 파일을 만드는 방법을 정리해 놓으려 한다.
Daemon : background Process를 말한다. 서비스 프로그램이다.
1. Controlling terminal
제어 터미널, 말 그대로 프로세스를 제어할 수 있는 터미널을 말한다.
우리가 ctrl+c, ctrl+z와 같이 인터럽트?를 발생시켜서 프로세스가 동작하는것을 강제로 멈춘다.
또한 xshell과 같은 프로그램으로 특정 터미널에서 프로세스를 동작시키고, 해당 터미널을 종료시키면
해당 프로세스는 죽게된다.
서비스 프로그램이 위와 같은 외부 변화에 의해서 종료된다면 안될 것이다.
그렇기 때문에 제어터미널을 삭제한다.
1.01 Controlling terminal Delete
제어 터미널을 없애기 위해서는 세션, 프로세스 그룹에 관한 이해가 필요하다.
세션을 구별하는 아이디 : sid[session id]
프로세스 그룹을 구별하는 아이디 : pgid [process group id]
프로세스를 구별하는 아이디 : pid [process id]
pgid에 관해서 좀 더 풀어서 설명한다.
process가 fork를 통해서 자식 프로세스를 생성했다고 가정한다. 그러면 부모 프로세스와 자식 프로세스 두개가 있다.
위 두 프로세스는 같은 그룹으로 간주해서 같은 pgid를 가진다.
하지만 부모 프로세스는 이 경우 해당 그룹의 리더?격이여서 pgid와 pid가 같다. --> 완장을 달아주는 느낌
반면에 자식 프로세는 위의 그룹의 일부이여서 pgid는 같지만 다른 pid를 같는다.
Daemon 프로세스(제어 터미널이 없는)가 되기 위해서는 세션 리더가 되어야 한다고 한다. --?? 왜 그렇지..
--> 추측하건데 세션의 리더가 되면 해당 세션은 자기것이 되기 때문에 다른것에 의한 간섭?이 발생할 수 없을 것이다.
세션 리더가 되기 위한 API[함수]는 setsid() 라는 함수이다. 하지만 이 함수는 조건이 필요한데 setsid()를 호출 하는
함수는 프로세스의 리더면 안된다고 한다.
--> 이것을 풀어서 설명?하면 부모 프로세스의 경우에는 setsid()함수를 호출할 수 없다. 자식 프로세스가 setsid() 함수를 호출!!
sid: 12322, pgid: 12826, pid: 12826, ppid: 12322 # start
sid: 12322, pgid: 12826, pid: 12827, ppid: 1 # after fork()
sid: 12827, pgid: 12827, pid: 12827, ppid: 1 # after setsid()
위의 부분에서 start 부분을 보면 pgid == pid 즉 프로세스 그룹의 리더이다. 부모 프로세스인데 sid != pid를 보면 세션 리더가 아님을 알 수있다.
그래서 부모 프로세스는 종료시키고 자식 프로세스를 생성하게된다.
부모 프로세스 exit 후 자식 프로세스를 생성한 부분이 after fork 부분인다.
보게 되면 pgid != pid 즉 프로세스 그룹의 리더가 아니다. 그러면 setsid 함수를 호출할 수 있다.
setsid()함수를 호출하고 나면 보게되면 sid == pgid == pid 즉, 해당 프로세스는 세션리더이자 프로세스 그룹의 리더가 되었다.
이를 통해서 터미널로 인한 변화를 막게 되었다.
1.02 Can't create Controlling terminal
실컷 제어 터미널을 삭제하면 무엇 하랴, 제어 터미널을 만들어 버리는 경우도 있는데.. --> 찾아봐야지..
이제 없앴으니 만들어지지 않게 해본다.
제어 터미널을 못 만들게 하려면, 현재 프로세스가 세션 그룹의 리더가 아니면 된다.
위에서 보면 sid == pgid == pid 즉 세션 리더였다.
그러니 이제 다시 fork를 통해서 자식 프로세스를 생성하고 부모 프로세스(처음 부모를 exit하고 생성된 자식 프로세스) exit하면 된다.
#include "stdio.h" #include "unistd.h" #include "errno.h" void print_id(const char *comment) { fprintf(stderr, "sid: %5d, pgid: %5d, pid: %5d, ppid: %5d # %s\n", (int)getsid(0), (int)getpgid(0), (int)getpid(), (int)getppid(), comment); } int main(void) { pid_t pid; print_id("start"); if ((pid = fork()) < 0) { fprintf(stderr, "fork() failed: %s\n", strerror(errno)); return 1; } else if (pid > 0) { /* parent */ _exit(0); } /* child */ print_id("after fork()"); if (setsid() == -1) { fprintf(stderr, "setsid() failed: %s\n", strerror(errno)); return 1; } print_id("after setsid()"); //두번째 자식을 생성 if((pid = fork()) <0) return 1; // 첫 부모의 자식 프로세스 종료 else if(pid > 0) _exit(0); // 자식의 자식 프로세스가 최종 결과물 print_id("second fork"); return 0; }
sid: 12322, pgid: 12865, pid: 12865, ppid: 12322 # start
sid: 12322, pgid: 12865, pid: 12866, ppid: 1 # after fork()
sid: 12866, pgid: 12866, pid: 12866, ppid: 1 # after setsid()
sid: 12866, pgid: 12866, pid: 12867, ppid: 1 # second fork
약간 이해를 편하게 해보면
할아버지 프로세스
아버지 프로세스
아들 프로세스
이렇게 있다 할아버지 프로세스를 종료하고 아버지 프로세스가 setsid를 하면 터미널을 컨트롤을 없애는거고
아버지 프로세스가 종료되고 아들 프로세스를 만들면 최종적인 데몬이 된다.
[출처] : http://cinsk.github.io/articles/daemon.html
'기타[etc]' 카테고리의 다른 글
Mips 환경 구축 (0) | 2017.03.29 |
---|---|
Makefile (0) | 2017.03.27 |
Android Forensic [/Data 추출] (0) | 2017.03.24 |
pydbg (0) | 2017.03.23 |
Makefile (0) | 2017.03.15 |