1、概念:守护进程是在后台运行的不受终端控制的进程,通常守护进程在系统启动时自动运行,守护进程的名称通常以d结尾,比如sshd、xinetd、crond等。
2、创建守护进程的步骤:
a、调用fork(),创建新进程,它会是将来的守护进程;
b、在父进程中调用exit(),保证子进程不是进程组组长;(进程组组长不能创建新的会话)
c、调用setsid创建新的会话期; pid_t setsid(void); 如果调用进程不是进程组组长,就可以创建一个新的会话期。调用者进程号码称为会话期的号码,和会话期中唯一进程组的号码
d、将当前目录改为根目录;
e、将标准输入、标准输出、标准错误重定向到/dev/null;
#include<unistd.h> #include<sys/types.h> #include<stdlib.h> #include<stdio.h> #include<errno.h> #include<string.h> #include<signal.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) //宏要求一条语句 int setup_daemon(void); int main() { setup_daemon();
for(;;); //实际上直接调用daemon()函数也可以实现。 //void daemon(int nochdir,int noclose); nochdir:0将当前目录更改至"/" //noclose:0 将标准输入、标准输出、标准错误重定向到/dev/null //printf("test...");//没有输出 return 0; } //创建完新会话,调用者进程将成为新会话期的领头进程; //调用者进程会成为新会话中唯一进程组的组长; //并且新的会话期没有控制终端;在后台运行; int setup_daemon(void) { pid_t pid; pid=fork(); if(pid==-1) ERR_EXIT("fork error"); else if(pid>0) exit(EXIT_SUCCESS); setsid(); chdir("/"); close(0); close(1); close(2); open("/dev/null",O_RDWR);//0指向/dev/null dup(0);//找到最小的1指向0 dup(0);//找到最小的2指向0 return 0; }
实际系统中提供了一个函数,实现上述功能:void daemon(int nochdir,int noclose);的参数都置为1,就不会重定向,也不会定位到根目录,主要用于开发调试的时候使用。
在 Debian 系统中, start-stop-daemon
就是为将一个普通程序变成守护进程而生。
-b, --background
通过 fork 和 setsid 的形式将程序变为后台运行。-d, --chdir
更改进程的工作目录。-u, --user
设置进程的执行用户。-k, --umask
设置新建文件的权限掩码。
除此之外, start-stop-daemon 还可以
-S, --start
启动程序-K, --stop
给程序发信号,终止程序或者判断程序的状态都可以
并且还通过 -p, --pidfile
和 -m, --make-pidfile
在启动程序时将守护进程启动后的 pid 写入指定文件,方便后续的终止程序或者判断程序的状态。
因为有了 start-stop-daemon
可以很容易写出系统启动脚本,网上的例子很多,比如这个
在我们的工作中,接触了很多守护进程(daemon),比如 Web Server (Apache,Nginx),MySQL,Redis,Memcached 等等。除了这些开源程序,我们自己也会开发一些守护进程以满足业务的需要,比如UU加速节点上的 Echo Server 用来给玩家客户端提供测速服务。那么此时,我们需要特别关心的是:一个完整的守护进程需要满足哪些特性,以及如何实现这些特性。
首先谈谈我们对于守护进程的要求和期望的特性:
- 后台 运行。
- 不会随着创建该守护进程的会话退出后,守护进程也跟着退出,要能 7x24 小时运行哇!
- 不能具有控制终端。杜绝从控制终端接收标准输入,还输出日志到控制终端。
在 《Advanced Programming in the UNIX Envrioment》一书中的 Chapter 13.Daemon Process ,就详细介绍 daemon 的编程规则和实现:
- 调用
fork
后,主进程退出,子进程忽略HUP信号。这样不仅能后台运行,还能忽略HUP信号,保证 7x24 小时运行。 - 调用
setsid
以创建一个新会话,使得调用进程:- 成为新会话的首进程
- 成为新进程组的组长进程
- 没有控制终端
这些操作可以使得一个程序满足了我们对于守护进程的期望,但是还远远不够,还需要:
- 调用
umask
设置权限掩码,保证守护进程创建新文件的权限。 - 调用
chdir
设置守护进程的工作目录。 - 调用
setuid
和setgid
设置守护进程的用户。 - 关闭从父进程继承的不再需要的文件描述符。
如果在我们的代码中去实现一个守护进程,确实费心费力。所以大家都在寻找如何将我们的一个简单的程序变成守护进程?在UU加速节点中,启动 Echo Server 守护进程时,比较粗暴的通过以下命令:
nohup command 2>&1 >> log &
这样的命令仅仅实现了后台运行和不随会话退出而提出,不仅很多细节没有实现,并且不够优雅。在 Debian 系统中, start-stop-daemon
就是为将一个普通程序变成守护进程而生。
-b, --background
通过 fork 和 setsid 的形式将程序变为后台运行。-d, --chdir
更改进程的工作目录。-u, --user
设置进程的执行用户。-k, --umask
设置新建文件的权限掩码。
除此之外, start-stop-daemon 还可以
-S, --start
启动程序-K, --stop
给程序发信号,终止程序或者判断程序的状态都可以
并且还通过 -p, --pidfile
和 -m, --make-pidfile
在启动程序时将守护进程启动后的 pid 写入指定文件,方便后续的终止程序或者判断程序的状态。
因为有了 start-stop-daemon
可以很容易写出系统启动脚本,网上的例子很多,比如这个 https://gist.github.com/alobato/1968852#file-start-stop-example-sh 。
start-stop-daemon在Debian系的Linux发行版中都是默认自带的。但在Redhat系Linux发行版中却没有该工具,我们可以自行安装:
wget -c http://developer.axis.com/download/distribution/apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
tar -xzf apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
cd apps/sys-utils/start-stop-daemon-IR1_9_18-2
gcc start-stop-daemon.c -o start-stop-daemon
切换到root下
cp start-stop-daemon /sbin/
chmod +x /sbin/start-stop-daemon