Linux daemon进程的应用、实现和原理
什么情况下用daemon进程
生产环境下,除了我们ssh登录上去,然后手动跑的那部分以外,其他都是自动运行的,这些大部分都应该是后台执行的。如何才能后台执行呢?
- nohup ./XXX &
- 由系统的其他daemon进程启动。这样的话,你的程序是它的子进程,跟终端没关系。退出终端也不会导致进程退出。如写在crontab里。
- 写成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 0 main pid : 7978 main parent pid : 1382 main pwd : /home/chenming [chenming@localhost ~]$ 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 [chenming@localhost ~]$ 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 [chenming@localhost ~]$ 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); } }
#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; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。