天天看點

Linux daemon程序的應用、實作和原理什麼情況下用daemon程序 怎麼用daemon程序怎麼寫daemon程序

什麼情況下用daemon程序

生産環境下,除了我們ssh登入上去,然後手動跑的那部分以外,其他都是自動運作的,這些大部分都應該是背景執行的。如何才能背景執行呢?

  1. nohup ./XXX & 
  2. 由系統的其他daemon程序啟動。這樣的話,你的程式是它的子程序,跟終端沒關系。退出終端也不會導緻程序退出。如寫在crontab裡。 
  3. 寫成daemon程式,可以手動執行,退出終端時程式不退出。

如何選擇呢?

(1)首先,清理過期日志這一類需求,可以寫一個死循環一直運作,也可以寫在crontab裡,每次執行完就退出(如果每分鐘一次可以滿足的話);

(2)crontab的需要接受最多1分鐘的時延,如果實時性要求更高一些,那麼就需要考慮寫個死循環了,這個程式可以由crontab來start和restart,隻有在挂了重新開機時才會出現1分鐘時延;

* * * * * /usr/bin/flock -nx /home/chenming/lock/test1.lock -c '/home/chenming/test1 >/dev/null 2>&1'
           

(3)服務不能中斷的(nginx、redis、apache,所有線上服務),一般都是daemon程式。但理論上用(2)似乎也可以;當然這兩者細節上有很多差別。

怎麼用daemon程序

linux C/C++可以直接調用int daemon(int, int)函數,不需要自己重新實作。示例代碼:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
 
int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: $0 no_ch_dir no_close_fd\n");
        exit(0);
    }

    int no_ch_dir = atoi(argv[1]);
    int no_close_fd = atoi(argv[2]);

    std::cout << "main pid : " << getpid() << std::endl;
    std::cout << "main parent pid : " << getppid() << std::endl;
    std::cout << "main pwd : " << get_current_dir_name() << std::endl;
    if (daemon(no_ch_dir, no_close_fd) != 0) {
        // 一般都用daemon(0,0)
        // 成功傳回0,失敗傳回-1
        // daemon(0,0):chdir到/,關閉0,1,2描述符。
        std::cout << "stdout: daemon = -1" << std::endl;
        std::cerr << "stderr: daemon = -1" << std::endl;
        return 1;
    }
    std::cout << "stdout: daemon = 0" << std::endl;
    std::cerr << "stderr: daemon = 0" << std::endl;
 
    std::cout << "sub pid : " << getpid() << std::endl;
    std::cout << "sub parent pid : " << getppid() << std::endl;
    std::cout << "sub pwd : " << get_current_dir_name() << std::endl;
    while (1);
    return 0;
}
           

編譯運作:

[[email protected] ~]$ g++ test1.cc -o test1
[[email protected] ~]$ ./test1 0 0
main pid : 7978
main parent pid : 1382
main pwd : /home/chenming
[[email protected] ~]$ ps -ef | grep test1
94:chenming  7864  7573  0 16:09 pts/0    00:00:16 vim test1.cc
95:chenming  7897     1 97 16:14 ?        00:33:07 ./test1 0 0
97:chenming  7979     1 47 16:48 ?        00:00:04 ./test1 0 0
99:chenming  7981  1382  8 16:49 pts/1    00:00:00 grep -inE --color test1
[[email protected] ~]$ ll /proc/7979/fd
total 0
lrwx------. 1 chenming chenming 64 May  1 16:49 0 -> /dev/null
lrwx------. 1 chenming chenming 64 May  1 16:49 1 -> /dev/null
lrwx------. 1 chenming chenming 64 May  1 16:49 2 -> /dev/null
[[email protected] ~]$ lsof -p 7979
COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
test1   7979 chenming  cwd    DIR  253,0     4096      2 /
test1   7979 chenming  rtd    DIR  253,0     4096      2 /
test1   7979 chenming  txt    REG  253,0     8355 142259 /home/chenming/test1
test1   7979 chenming  mem    REG  253,0   122232  52742 /lib/libgcc_s-4.4.7-20120601.so.1
test1   7979 chenming  mem    REG  253,0   142600  38788 /lib/ld-2.12.so
test1   7979 chenming  mem    REG  253,0  1906308  38865 /lib/libc-2.12.so
test1   7979 chenming  mem    REG  253,0   202040  47921 /lib/libm-2.12.so
test1   7979 chenming  mem    REG  253,0   942040  52866 /usr/lib/libstdc++.so.6.0.13
test1   7979 chenming    0u   CHR    1,3      0t0   3903 /dev/null
test1   7979 chenming    1u   CHR    1,3      0t0   3903 /dev/null
test1   7979 chenming    2u   CHR    1,3      0t0   3903 /dev/null
           

man 3 daemon可以檢視到函數簽名:

#include <unistd.h>

int daemon(int nochdir, int noclose);

怎麼寫daemon程序

1.fork,父程序退出

2.setsid。跟終端脫離關系。

3.umask、關掉0,1,2檔案描述符。

4.chdir

5.信号處理

這裡隻有1和 2是必須的。3一般都會做;5大部分不做。

自己實作一個:

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

int daem(int no_ch_dir, int no_close_fd) {
    int pid = fork();
    if (pid < 0) {
        return -1;
    }else if (pid > 0){
        exit(0);
    }

    if (setsid() < 0) {
        return -1;
    }

    if (no_ch_dir == 0) {
        chdir("/");
    }

    umask(0);

    if (no_close_fd == 0) {
        close(0);
        close(1);
        close(2);
    }
    return 0;
}
           

redis的實作:

void daemonize(void) {
    int fd;

    if (fork() != 0) exit(0); /* parent exits */
    setsid(); /* create a new session */

    /* Every output goes to /dev/null. If Redis is daemonized but
     * the 'logfile' is set to 'stdout' in the configuration file
     * it will not log at all. */
    if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
        if (fd > STDERR_FILENO) close(fd);
    }
}
           

memcached的實作:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "memcached.h"

int daemonize(int nochdir, int noclose)
{
    int fd;

    switch (fork()) {
    case -1:
        return (-1);
    case 0:
        break;
    default:
        _exit(EXIT_SUCCESS);
    }

    if (setsid() == -1)
        return (-1);

    if (nochdir == 0) {
        if(chdir("/") != 0) {
            perror("chdir");
            return (-1);
        }
    }

    if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
        if(dup2(fd, STDIN_FILENO) < 0) {
            perror("dup2 stdin");
            return (-1);
        }
        if(dup2(fd, STDOUT_FILENO) < 0) {
            perror("dup2 stdout");
            return (-1);
        }
        if(dup2(fd, STDERR_FILENO) < 0) {
            perror("dup2 stderr");
            return (-1);
        }

        if (fd > STDERR_FILENO) {
            if(close(fd) < 0) {
                perror("close");
                return (-1);
            }
        }
    }
    return (0);
}
           

nginx的實作:

#include <ngx_config.h>
#include <ngx_core.h>


ngx_int_t
ngx_daemon(ngx_log_t *log)
{
    int  fd;

    switch (fork()) {
    case -1:
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
        return NGX_ERROR;

    case 0:
        break;

    default:
        exit(0);
    }

    ngx_pid = ngx_getpid();

    if (setsid() == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
        return NGX_ERROR;
    }

    umask(0);

    fd = open("/dev/null", O_RDWR);
    if (fd == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "open(\"/dev/null\") failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDIN_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
        return NGX_ERROR;
    }

#if 0
    if (dup2(fd, STDERR_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
        return NGX_ERROR;
    }
#endif

    if (fd > STDERR_FILENO) {
        if (close(fd) == -1) {
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}