Linux daemon进程的应用、实现和原理

来源:互联网 发布:炒外汇交易软件 编辑:程序博客网 时间:2024/05/02 02:45

什么情况下用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;}

编译运行:

[chenming@localhost ~]$ g++ test1.cc -o test1[chenming@localhost ~]$ ./test1 0 0main pid : 7978main parent pid : 1382main pwd : /home/chenming[chenming@localhost ~]$ ps -ef | grep test194:chenming  7864  7573  0 16:09 pts/0    00:00:16 vim test1.cc95:chenming  7897     1 97 16:14 ?        00:33:07 ./test1 0 097:chenming  7979     1 47 16:48 ?        00:00:04 ./test1 0 099:chenming  7981  1382  8 16:49 pts/1    00:00:00 grep -inE --color test1[chenming@localhost ~]$ ll /proc/7979/fdtotal 0lrwx------. 1 chenming chenming 64 May  1 16:49 0 -> /dev/nulllrwx------. 1 chenming chenming 64 May  1 16:49 1 -> /dev/nulllrwx------. 1 chenming chenming 64 May  1 16:49 2 -> /dev/null[chenming@localhost ~]$ lsof -p 7979COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF   NODE NAMEtest1   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/test1test1   7979 chenming  mem    REG  253,0   122232  52742 /lib/libgcc_s-4.4.7-20120601.so.1test1   7979 chenming  mem    REG  253,0   142600  38788 /lib/ld-2.12.sotest1   7979 chenming  mem    REG  253,0  1906308  38865 /lib/libc-2.12.sotest1   7979 chenming  mem    REG  253,0   202040  47921 /lib/libm-2.12.sotest1   7979 chenming  mem    REG  253,0   942040  52866 /usr/lib/libstdc++.so.6.0.13test1   7979 chenming    0u   CHR    1,3      0t0   3903 /dev/nulltest1   7979 chenming    1u   CHR    1,3      0t0   3903 /dev/nulltest1   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_tngx_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;}




0 0
原创粉丝点击