Programming

Linux에서 데몬 만들기

procodes 2020. 8. 27. 22:03
반응형

Linux에서 데몬 만들기


Linux에서는 중지 할 수없고 파일 시스템 변경을 모니터링하는 데몬을 추가하고 싶습니다. 변경 사항이 감지되면 시작된 콘솔 경로와 개행 문자를 기록해야합니다.

이미 파일 시스템 변경 코드가 거의 준비되어 있지만 데몬을 만드는 방법을 알 수 없습니다.

내 코드는 여기에 있습니다 : http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

포크 후에 무엇을해야합니까?

int main (int argc, char **argv) {

  pid_t pID = fork();
  if (pID == 0)  {              // child
          // Code only executed by child process    
      sIdentifier = "Child Process: ";
    }
    else if (pID < 0) {
        cerr << "Failed to fork" << endl;
        exit(1);
       // Throw exception
    }
    else                                   // parent
    {
      // Code only executed by parent process

      sIdentifier = "Parent Process:";
    }       

    return 0;
}

Linux에서 중지 할 수없고 파일 시스템 변경을 모니터링하는 데몬을 추가하고 싶습니다. 변경 사항이 감지되면 시작된 콘솔 경로 + 개행 문자를 작성해야합니다.

데몬은 백그라운드에서 작동하며 (보통 ...) TTY에 속하지 않기 때문에 원하는 방식으로 stdout / stderr를 사용할 수 없습니다. 일반적으로 syslog 데몬 ( syslogd )은 파일 (디버그, 오류 등)에 메시지를 기록하는 데 사용됩니다.

그 외에도 프로세스를 데몬 화하는 데 필요한 몇 가지 단계 가 있습니다.


올바르게 기억하는 경우 다음 단계는 다음과 같습니다.

  • 부모 프로세스를 포크 하고 포크가 성공하면 종료합니다. -> 상위 프로세스가 종료되었으므로 이제 하위 프로세스가 백그라운드에서 실행됩니다.
  • setsid- 새 세션을 만듭니다. 호출 프로세스는 새 세션의 리더가되고 새 프로세스 그룹의 프로세스 그룹 리더가됩니다. 이제 프로세스가 제어 터미널 (CTTY)에서 분리됩니다.
  • 신호 포착 - 신호를 무시 및 / 또는 처리합니다.
  • 다시 포크 하고 부모 프로세스를 종료하여 세션 선행 프로세스를 제거하십시오. (세션 리더 만이 TTY를 다시받을 수 있습니다.)
  • chdir- 데몬의 작업 디렉토리를 변경합니다.
  • umask- 데몬의 필요에 따라 파일 모드 마스크를 변경합니다.
  • close- 상위 프로세스에서 상속 될 수있는 모든 열린 파일 설명자를 닫 습니다 .

시작점을 제공하려면 : 기본 단계를 보여주는 스켈레톤 코드를보십시오. 이 코드는 이제 GitHub : Linux 데몬의 기본 골격 에서도 분기 할 수 있습니다.

/*
 * daemonize.c
 * This example daemonizes a process, writes a few log messages,
 * sleeps 20 seconds and terminates afterwards.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>

static void skeleton_daemon()
{
    pid_t pid;

    /* Fork off the parent process */
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);

    /* Catch, ignore and handle signals */
    //TODO: Implement a working signal handler */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    /* Fork off for the second time*/
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* Set new file permissions */
    umask(0);

    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");

    /* Close all open file descriptors */
    int x;
    for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
    {
        close (x);
    }

    /* Open the log file */
    openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
    skeleton_daemon();

    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }

    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();

    return EXIT_SUCCESS;
}


  • 코드를 컴파일합니다. gcc -o firstdaemon daemonize.c
  • 데몬을 시작합니다. ./firstdaemon
  • 모든 것이 제대로 작동하는지 확인하십시오. ps -xj | grep firstdaemon

  • 출력은 다음과 유사해야합니다.

+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +
| PPID | PID | PGID | SID | TTY | TPGID | 통계 | UID | 시간 | CMD |
+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +
| 1 | 3387 | 3386 | 3386 | ? | -1 | S | 1000 | 0:00 | ./ |
+ ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +

여기서 확인해야 할 것은 다음과 같습니다.

  • 데몬에는 제어 터미널이 없습니다 ( TTY =? )
  • 상위 프로세스 ID ( PPID )는 1 (초기 프로세스)입니다.
  • PID! = SID 우리의 프로세스가 세션 리더되지 않음을 의미합니다
    (때문에 두 번째 포크 ())
  • PID! = SID 때문에 우리 프로세스 는 TTY를 다시 제어 할 수 없습니다.

syslog 읽기 :

  • syslog 파일을 찾습니다. 내 위치 :/var/log/syslog
  • 다음을 수행하십시오. grep firstdaemon /var/log/syslog

  • 출력은 다음과 유사해야합니다.

  firstdaemon [3387] : 첫 번째 데몬이 시작되었습니다.
  firstdaemon [3387] : 첫 번째 데몬이 종료되었습니다.


참고 : 실제로 신호 처리기를 구현하고 로깅을 적절하게 설정해야합니다 (파일, 로그 수준 ...).

추가 읽기 :


man 7 daemon데몬을 만드는 방법을 자세히 설명합니다. 내 대답은이 설명서에서 발췌 한 것입니다.

데몬에는 최소한 두 가지 유형이 있습니다.

  1. 기존 SysV 데몬 ( 구식 ),
  2. systemd 데몬 ( new-style ).

SysV 데몬

기존 SysV 데몬에 관심이있는 경우 다음 단계를 구현해야 합니다 .

  1. 표준 입력 , 출력오류를 제외한 모든 열린 파일 설명자를 닫습니다 (즉, 처음 세 개의 파일 설명자 0, 1, 2). 이는 실수로 전달 된 파일 설명자가 데몬 프로세스에 남아 있지 않도록합니다. Linux에서는 /proc/self/fd파일 설명자 3에서 getrlimit()for에서 반환 한 값으로의 반복을 대체 하여를 반복하여 구현하는 것이 가장 좋습니다 RLIMIT_NOFILE.
  2. 모든 신호 처리기를 기본값으로 재설정 합니다. 사용 가능한 신호를 최대 한도까지 반복 _NSIG하고로 재설정하는 것이 가장 좋습니다 SIG_DFL.
  3. 를 사용하여 신호 마스크를 재설정합니다 sigprocmask().
  4. 데몬 런타임에 부정적인 영향을 미칠 수있는 환경 변수를 제거하거나 재설정하여 환경 블록을 삭제합니다.
  5. 를 호출 fork()하여 백그라운드 프로세스를 만듭니다.
  6. 자식 setsid()에서 모든 터미널에서 분리하고 독립적 인 세션을 만들기 위해 호출 합니다 .
  7. 자식에서 fork()다시 호출 하여 데몬이 터미널을 다시 획득 할 수 없도록합니다.
  8. exit()첫 번째 자식을 호출 하여 두 번째 자식 (실제 데몬 프로세스) 만 유지되도록합니다. 이렇게하면 모든 데몬이 그래야하는 것처럼 데몬 프로세스가 init / PID 1의 상위가됩니다.
  9. 데몬 프로세스에서 /dev/null표준 input , outputerror에 연결 합니다 .
  10. 데몬 과정에서,를 다시 umask파일 모드가 통과 그래서, 0 open(), mkdir()직접 만든 파일과 디렉토리의 액세스 모드를 제어 그와 같은.
  11. 데몬 프로세스 에서 현재 디렉토리를 루트 디렉토리 ( ) 변경/ 하여 데몬이 마운트 지점이 마운트 해제되는 것을 비자발적으로 차단하지 않도록합니다.
  12. 데몬 프로세스에서 데몬 PID (에서 반환 된대로 getpid())를 PID 파일 /run/foobar.pid( : 가상 데몬 "foobar"의 경우)에 기록하여 데몬이 두 번 이상 시작되지 않도록합니다. PID 파일에 이전에 저장된 PID가 더 이상 존재하지 않거나 외부 프로세스에 속함과 동시에 PID 파일이 확인 될 때만 PID 파일이 업데이트되도록 경쟁없는 방식으로 구현해야합니다.
  13. 데몬 프로세스에서 가능하고 적용 가능한 경우 권한을 삭제합니다.
  14. 데몬 프로세스에서 초기화가 완료되었음을 시작된 원래 프로세스에 알립니다. 이것은 이름이 지정되지 않은 파이프 또는 첫 번째 이전에 생성 된 유사한 통신 채널을 통해 구현 될 수 있으므로 fork()원본 및 데몬 프로세스 모두에서 사용할 수 있습니다.
  15. exit()원래 프로세스를 호출 하십시오. 데몬을 호출 한 프로세스는 초기화가 완료되고 모든 외부 통신 채널이 설정되고 액세스 가능한 후에exit() 발생 한다는 사실에 의존 할 수 있어야합니다 .

다음 경고에 유의하십시오.

BSD daemon()함수 이러한 단계 하위 집합 만 구현하므로 사용 해서는 안됩니다 .

SysV 시스템과의 호환성 을 제공해야하는 데몬은 위에서 언급 한 체계를 구현해야합니다. 그러나 디버깅을 용이하게하고 systemd를 사용하여 시스템으로의 통합을 단순화하기 위해이 동작을 선택 사항으로 만들고 명령 줄 인수를 통해 구성 할 수 있도록하는 것이 좋습니다.

참고 daemon()없는 POSIX의 준수.


새로운 스타일의 데몬

새로운 스타일의 데몬의 경우 다음 단계 를 권장합니다.

  1. 경우 SIGTERM수신 깨끗하게 데몬 종료를 종료합니다.
  2. SIGHUP수신 된 경우 구성 파일을 다시로드하십시오 (적용되는 경우).
  3. 서비스 오류 및 문제를 감지하기 위해 init 시스템에서 사용하므로 기본 데몬 프로세스에서 올바른 종료 코드를 제공하십시오. SysV init 스크립트 에 대한 LSB 권장 사항에 정의 된 종료 코드 체계를 따르는 것이 좋습니다 .
  4. 가능하고 적용 가능한 경우 D-Bus IPC 시스템을 통해 데몬의 제어 인터페이스를 노출하고 초기화의 마지막 단계로 버스 이름을 가져옵니다.
  5. systemd에서 통합 하려면 데몬 시작, 중지 및 유지 관리에 대한 정보를 전달 하는 .service unit 파일을 제공 하십시오. 자세한 내용은 systemd.service(5)을 참조하십시오.
  6. 가능한 한 init 시스템의 기능에 의존하여 파일, 서비스 및 기타 리소스에 대한 데몬 액세스를 제한합니다. 즉, systemd의 경우 자체 구현 대신 systemd의 리소스 제한 제어 에 의존하고 systemd의 권한 삭제 에 의존합니다. 데몬에서 구현하는 대신 코드와 유사합니다. systemd.exec(5)사용 가능한 컨트롤 참조하십시오 .
  7. 경우 D-버스가 사용하는 D-버스 서비스 활성화에 공급하여 데몬 버스 - 기동하게 구성 파일을 . 여기에는 여러 가지 장점이 있습니다. 데몬이 요청시 느리게 시작될 수 있습니다. 필요로하는 다른 데몬과 병렬로 시작될 수 있습니다. 이는 병렬화 및 부팅 속도 를 최대화 합니다 . 버스가 활성화 가능한 서비스에 대한 요청을 대기열에 추가하므로 버스 요청을 잃지 않고 실패시 데몬을 다시 시작할 수 있습니다. 자세한 내용은 아래 를 참조하십시오.
  8. 데몬이 소켓을 통해 다른 로컬 프로세스 나 원격 클라이언트에 서비스를 제공하는 경우 아래에 설명 된 체계에 따라 소켓 활성화 가 가능해야 합니다 . D-Bus 활성화와 마찬가지로 이것은 서비스 시작의 병렬화를 개선 할뿐만 아니라 주문형 서비스 시작을 가능하게합니다. 또한 상태 비 저장 프로토콜 (예 : syslog, DNS)의 경우 단일 요청 손실없이 소켓 기반 활성화를 구현하는 데몬을 다시 시작할 수 있습니다. 자세한 내용은 아래 를 참조하십시오.
  9. 해당되는 경우 데몬은 sd_notify(3)인터페이스 를 통해 시작 완료 또는 상태 업데이트에 대해 init 시스템에 알려야합니다 .
  10. syslog()호출을 사용하여 시스템 syslog 서비스에 직접 기록 하는 대신 새로운 스타일의 데몬은를 통해 단순히 표준 오류에 기록하도록 선택할 수 있습니다. fprintf()그런 다음 init 시스템에 의해 syslog로 전달됩니다. 로그 수준이 필요한 경우 Linux 커널 printk()수준 시스템 과 유사한 스타일에 따라 "<4>"(syslog 우선 순위 체계의 로그 수준 4 "WARNING")와 같은 문자열로 개별 로그 행을 접두사로 지정하여 이러한 수준을 인코딩 할 수 있습니다 . 자세한 내용은 sd-daemon(3)및을 참조하십시오 systemd.exec(5).

자세한 내용은 전체를 읽으십시오 man 7 daemon.


리눅스에서는 죽일 수없는 프로세스를 만들 수 없습니다. 루트 사용자 (uid = 0)는 프로세스에 신호를 보낼 수 있으며 잡을 수없는 신호는 SIGKILL = 9, SIGSTOP = 19입니다. 그리고 다른 신호 (잡히지 않은 경우)도 프로세스를 종료 할 수 있습니다.

프로그램 / 데몬의 이름과 프로그램을 실행할 경로 (예 : "/"또는 "/ tmp")를 지정할 수있는보다 일반적인 데몬 화 기능이 필요할 수 있습니다. stderr 및 stdout (및 stdin을 사용하는 제어 경로)에 대한 파일을 제공 할 수도 있습니다.

필요한 것은 다음과 같습니다.

#include <stdio.h>    //printf(3)
#include <stdlib.h>   //exit(3)
#include <unistd.h>   //fork(3), chdir(3), sysconf(3)
#include <signal.h>   //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h>   //syslog(3), openlog(3), closelog(3)

여기 더 일반적인 기능이 있습니다.

int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
    if(!path) { path="/"; }
    if(!name) { name="medaemon"; }
    if(!infile) { infile="/dev/null"; }
    if(!outfile) { outfile="/dev/null"; }
    if(!errfile) { errfile="/dev/null"; }
    //printf("%s %s %s %s\n",name,path,outfile,infile);
    pid_t child;
    //fork, detach from process group leader
    if( (child=fork())<0 ) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if (child>0) { //parent
        exit(EXIT_SUCCESS);
    }
    if( setsid()<0 ) { //failed to become session leader
        fprintf(stderr,"error: failed setsid\n");
        exit(EXIT_FAILURE);
    }

    //catch/ignore signals
    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //fork second time
    if ( (child=fork())<0) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if( child>0 ) { //parent
        exit(EXIT_SUCCESS);
    }

    //new file permissions
    umask(0);
    //change to path directory
    chdir(path);

    //Close all open file descriptors
    int fd;
    for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
    {
        close(fd);
    }

    //reopen stdin, stdout, stderr
    stdin=fopen(infile,"r");   //fd=0
    stdout=fopen(outfile,"w+");  //fd=1
    stderr=fopen(errfile,"w+");  //fd=2

    //open syslog
    openlog(name,LOG_PID,LOG_DAEMON);
    return(0);
}

다음은 데몬이되어 주위를 맴돌다가 떠나는 샘플 프로그램입니다.

int
main()
{
    int res;
    int ttl=120;
    int delay=5;
    if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
        fprintf(stderr,"error: daemonize failed\n");
        exit(EXIT_FAILURE);
    }
    while( ttl>0 ) {
        //daemon code here
        syslog(LOG_NOTICE,"daemon ttl %d",ttl);
        sleep(delay);
        ttl-=delay;
    }
    syslog(LOG_NOTICE,"daemon ttl expired");
    closelog();
    return(EXIT_SUCCESS);
}

SIG_IGN은 신호를 포착하고 무시 함을 나타냅니다. 신호 수신을 기록 할 수있는 신호 처리기를 빌드하고 플래그 (예 : 정상 종료를 나타내는 플래그)를 설정할 수 있습니다.


" 멈출 수없는 데몬 ..." 이라는 첫 번째 요구 사항에서 멈출 수 있습니다 .

내 친구는 불가능합니다. 그러나 훨씬 더 나은 도구 인 커널 모듈을 사용하여 동일한 결과를 얻을 수 있습니다.

http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring

All daemons can be stopped. Some are more easily stopped than others. Even a daemon pair with the partner in hold down, respawning the partner if lost, can be stopped. You just have to work a little harder at it.


Try using the daemon function:

#include <unistd.h>

int daemon(int nochdir, int noclose);

From the man page:

The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons.

If nochdir is zero, daemon() changes the calling process's current working directory to the root directory ("/"); otherwise, the current working directory is left unchanged.

If noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise, no changes are made to these file descriptors.


If your app is one of:

{
  ".sh": "bash",
  ".py": "python",
  ".rb": "ruby",
  ".coffee" : "coffee",
  ".php": "php",
  ".pl" : "perl",
  ".js" : "node"
}

and you don't mind a NodeJS dependency then install NodeJS and then:

npm install -g pm2

pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above

pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores

pm2 list

To keep all apps running on reboot (and daemonise pm2):

pm2 startup

pm2 save

Now you can:

service pm2 stop|restart|start|status

(also easily allows you to watch for code changes in your app directory and auto restart the app process when a code change happens)


By calling fork() you've created a child process. If the fork is successful (fork returned a non-zero PID) execution will continue from this point from within the child process. In this case we want to gracefully exit the parent process and then continue our work in the child process.

Maybe this will help: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html


A daemon is just a process in the background. If you want to start your program when the OS boots, on linux, you add your start command to /etc/rc.d/rc.local (run after all other scripts) or /etc/startup.sh

On windows, you make a service, register the service, and then set it to start automatically at boot in administration -> services panel.

참고URL : https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux

반응형