Linux中创建Daemon进程的三种方法

什么是daemon进程?

Unix/Linux中的daemon进程类似于Windows中的后台服务进程,一直在后台运行运行,例如http服务进程nginx,ssh服务进程sshd等。
注意,其英文拼写为daemon而不是deamon。

为什么daemon进程需要特殊的编写步骤?

daemon进程和普通进程不一样吗?为什么要单独提出如何编写daemon进程呢?
不知道你是否有过这样的经历,在Linux上面打开一个terminal,输入编译命令进行编译,编译的时间可能比较长,
这时候你不小心关闭了这个terminal,编译就中断了。因为编译脚本是作为当前terminal的一个子进程来执行的,当terminal退出后,
子进程也就退出了。而作为daemon进程,我们希望一旦启动就能在后台一直运行,不会随着terminal的退出而结束。
那么如何能做到这一点呢?有人说用下面的命令行吗?

> make &

让编译命令make到后台执行,这样只是造成了make在后台一直运行的假象,它依然没有脱离和terminal之间的父子关系;
当terminal退出后,make依然会退出。所以针对daemon进程就要用特殊的步骤来编写,以保证在terminal中执行后,
即使terminal退出,daemon进程仍然在后台运行。

如何编写daemon进程?

对于可以用多种方法解决的问题,我们一般只需熟练掌握其中一种最适合自己的即可;
但是需要知道还有其它的方法,以备不时之需,这里我将介绍三种创建daemon进程的方法。

1. 首先给出经典名著APUE中的方法:

#include "apue.h"
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>void daemonize(const char *cmd){int i, fd0, fd1, fd2;pid_t pid;struct rlimit rl;struct sigaction sa;/* * Clear file creation mask. */umask(0);//注释1/* * Get maximum number of file descriptors. */if (getrlimit(RLIMIT_NOFILE, &rl) < 0)err_quit("%s: can't get file limit", cmd);/* * Become a session leader to lose controlling TTY. */if ((pid = fork()) < 0)//注释2err_quit("%s: can't fork", cmd);else if (pid != 0) /* parent */exit(0);setsid();//注释3/* * Ensure future opens won't allocate controlling TTYs. */sa.sa_handler = SIG_IGN;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;if (sigaction(SIGHUP, &sa, NULL) < 0)err_quit("%s: can't ignore SIGHUP", cmd);if ((pid = fork()) < 0)//注释4err_quit("%s: can't fork", cmd);else if (pid != 0) /* parent */exit(0);/* * Change the current working directory to the root so * we won't prevent file systems from being unmounted. */if (chdir("/") < 0)//注释5err_quit("%s: can't change directory to /", cmd);/* * Close all open file descriptors. */if (rl.rlim_max == RLIM_INFINITY)rl.rlim_max = 1024;for (i = 0; i < rl.rlim_max; i++)close(i);//注释6/* * Attach file descriptors 0, 1, and 2 to /dev/null. */fd0 = open("/dev/null", O_RDWR);//注释7fd1 = dup(0);//注释7fd2 = dup(0);//注释7/* * Initialize the log file. */openlog(cmd, LOG_CONS, LOG_DAEMON);if (fd0 != 0 || fd1 != 1 || fd2 != 2) {syslog(LOG_ERR, "unexpected file descriptors %d %d %d",fd0, fd1, fd2);exit(1);}
}

下面是针对上面例子的详细解释:

* 注释1:因为我们从shell创建的daemon子进程,所以daemon子进程会继承shell的umask,如果不清除的话,会导致daemon进程创建文件时屏蔽某些权限。
* 注释2:fork后让父进程退出,子进程获得新的pid,肯定不为进程组组长,这是setsid前提。
* 注释3:调用setsid来创建新的进程会话。这使得daemon进程成为会话首进程,脱离和terminal的关联。
* 注释4:最好在这里再次fork。这样使得daemon进程不再是会话首进程,那么永远没有机会获得控制终端。如果这里不fork的话,会话首进程依然可能打开控制终端。
* 注释5:将当前工作目录切换到根目录。父进程继承过来的当前目录可能mount在一个文件系统上,如果不切换到根目录,那么这个文件系统不允许unmount。
* 注释6:在子进程中关闭从父进程中继承过来的那些不需要的文件描述符。可以通过_SC_OPEN_MAX来判断最高文件描述符(不是很必须).
* 注释7:打开/dev/null复制到0,1,2,因为dameon进程已经和terminal脱离了,所以需要重新定向标准输入,标准输出和标准错误(不是很必须).

针对这个例子,首先要说明的是,不管在Unix还是Linux上按照这个例子写的daemon肯定没问题。
不过我对其中的一些步骤的必要性一直持怀疑态度:

1) 第二个fork是必须的吗?
根据APUE中的说法是,这是为了防止后面打开终端的时候又关联到了daemon进程上,这样当终端关闭后,daemon进程就退出了,
不过个人感觉这种说法有可能已经不再适用了,毕竟大名鼎鼎的nginx也没有fork两次。不过目前我还不知道怎么用实验来证明这个结论。
2) setsid()是必须的吗?
按照书上说的是每个进程都属于一个进程组(Process Group),每个进程组都属于一个进程会话(Process Session)。
这三者的关系如下图所示,当terminal退出的时候,以最初login shell为首的进程回话就结束了。
这时候,属于这个session的所有进程都会收到SIGHUP信号,导致进程退出。
执行了第一次fork(),父进程退出了,子进程变成孤儿进程过继给了1号init进程,但是它仍然属于当前登录shell所控制的session,
调用setsid()的目的是让daemon进程形成独立的Session,这样当terminal退出的时候就影响不到这个daemon进程了。
但是我在各种Unix,Linux系统上做了实验,不调用setsid(), 并且只fork()一次,然后将当前终端关闭,重新打开一个新的终端,
发现daemon进程仍然存在,并没有像书中所说会随着terminal的退出而退出,请高人指点迷津。

2. 利用系统库函数daemon()创建daemon进程

Linux系统还专门提供了一个用来创建daemon进程的系统函数:

int daemon(int nochdir, int noclose);

从api的文档描述看该api也调用了fork(),估计内部实现和上面的代码逻辑类似,从其参数作用也可以看出这一点,
这个api有两个参数,其作用分别对应上面代码中的注释5和注释7。下面是用这个api创建daemon进程的简单示例:

#include <unistd.h>
#include <stdlib.h>int main(void)
{if(daemon(0,0) == -1)exit(EXIT_FAILURE);while(1){sleep(60);}return 0;
}

3. 使用第三方工具supervisor

简单的说supervisor是一个python工具,可以通过编写配置文件来对指定的进程进行管理,比如启动进程,停止进程以及进程退出后自动重启等;
这样一来,即使一个普通进程通过supervisor管理之后也会变成和daemon进程一样的行为,不会随着terminal的关闭而退出。
后续我会写一篇关于supervisor的文章专门介绍其具体使用方法。

参考资料

http://www.cnblogs.com/mickole/p/3188321.html
https://dirtysalt.github.io/apue.html#orgheadline174

posted on 2017-10-20 22:22 都市码农 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/minico/p/7702020.html

Linux中创建Daemon进程的三种方法相关推荐

  1. Linux中增加软路由的两种方法,Linux中增加软路由的三种方法

    # route add –net IP netmask MASK eth0 # route add –net IP netmask MASK gw IP # route add –net IP/24 ...

  2. 网络编程(32)—— linux中销毁僵尸进程的四种方法

    一.wait函数 函数原型: pid_t wait(int *status); 描述: wait可以回收任意一个僵尸进程,只要系统中存在僵尸进程,调用一次wait,就会回收一个僵尸进程. 参数说明: ...

  3. Unity中创建游戏物体的三种方法:

    通过new GameObject(); 通过Instantiate(): 通过GameObject.CreatePrimitive(): 总结: 1)new GameObject()可通过字符串名字来 ...

  4. 【export】Linux中export命令介绍,三种方法设置环境变量

    ✨ 因为一个变量创建时,它不会自动的被在它之后创建的shell进程所知:这时,可用export命令向后面的shell传递变量的值.

  5. Linux 中设置环境变量的三种方法

    1.export 变量名=value 2.变量名=value ; export 变量名 3.declare -x 变量名=value 自定义全局环境变量 1.编辑 /etc/profile vim / ...

  6. linux系统中清空文件内容的三种方法

    1.使用vi/vim命令打开文件后,输入"%d"清空,后保存即可.但当文件内容较大时,处理较慢,命令如下: vim file_name :%d :wq 2.使用cat命令情况,命令 ...

  7. Linux中执行shell脚本的5种方法总结

    Linux中执行shell脚本的4种方法总结,即在Linux中运行shell脚本的4种方法: 方法一:切换到shell脚本所在的目录(此时,称为工作目录)执行shell脚本: 复制代码 代码如下: c ...

  8. Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法...

    Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法 www.MyException.Cn   发布于:2012-09-15 ...

  9. Linux中执行shell脚本的4种方法

    这篇文章主要介绍了Linux中执行shell脚本的4种方法总结,即在Linux中运行shell脚本的4种方法,需要的朋友可以参考下. bash shell 脚本的方法有多种,现在作个小结.假设我们编写 ...

最新文章

  1. 报错-Unknown class in Interface Builder file
  2. Dockerizing a Node.js web app
  3. DLL基础——Windows核心编程学习手札之十九
  4. python输出去空格_Python3基础 print(,end=) 输出内容的末尾加入空格
  5. 一个鉴黄师的产品之路(11-12更新)
  6. java在src创建entity文件_java自动生成entity文件
  7. 深度神经网络DNN的多GPU数据并行框架 及其在语音识别的应用
  8. Net 分页功能的实现
  9. BHO插件操作IE浏览器,js调用C#方法
  10. HDU 5517---Triple(二维树状数组)
  11. sublime搭建C/C++编译环境
  12. 三行代码隐藏所有console.log
  13. vant ui 官方文档_转载:微信小程序UI组件库合集
  14. CodeForces - 245H Palindromes区间dp_记忆化搜索
  15. JAVA 反射(JAVA面试题)
  16. 无法弹出U盘的几种解决方法
  17. 64位平台C/C++开发注意事项
  18. 产品思考 - 不剪发的Tony老师
  19. 2021.03.06【NOIP提高B组】模拟 总结
  20. ——java中的反射

热门文章

  1. Chapter7 对称密钥原语的实用构造
  2. 动态规划最优二叉搜索树C语言,算法 – 动态规划:最优二叉搜索树
  3. 计算机高新技术学什么,全国计算机信息高新技术证书是什么?如何考?
  4. ASO搜索指数是什么?对应搜索量吗?
  5. 手机4g信号强的服务器,4G信号不好?手把手教你解决手机信号问题!
  6. 暑假如何有效控制、预防近视?保护视力的护眼灯分享
  7. 红象云腾CRH 一键部署大数据平台
  8. ZZULIOJ【1091】童年生活二三事【斐波那契】
  9. 10行的一个窗体,IUP框架
  10. Python fractions 模块介绍