1、守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程。
2、创建守护进程步骤
1)创建子进程,父进程退出
之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离。
在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。
2)在子进程中创建新会话
进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID。且该进程组ID不会因组长进程的退出而受到影响。
会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
(1)pid_t setsid(void);
setsid() creates a new session if the calling process is not a process group leader. The calling process will be the only process in this new process group and in this new session.
setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个作用:
① 让进程摆脱原会话的控制
② 让进程摆脱原进程组的控制
③ 让进程摆脱原控制终端的控制
有以下三个结果:
(a)成为新会话的首进程
(b)成为一个新进程组的组长进程
(c)没有控制终端。
有些人建议在此时再次调用fork,并使父进程终止。第二个子进程作为守护进程继续运行。这样就保证了该守护进程不是会话首进程。【4】
setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。
setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
使用fork创建的子进程继承了父进程的当前工作目录;进程活动时,其工作目录所在的文件系统不能卸下。通常的做法是让"/"作为守护进程的当前工作目录,也可以是其他目录,如/tmp,使用chdir。
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。mask = mask & ~050
通常,把文件权限掩码设置为0,umask(0)。
用fork函数新建的子进程会从父进程那里继承已经打开了的文件描述符。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。
在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。
for(i=0;i<MAXFILE;i++)
close(i);
当用户需要外部停止守护进程运行时,往往会使用 kill命令停止该守护进程。所以,守护进程中需要编码来实现kill发出的signal信号处理,达到进程的正常退出。
signal(SIGTERM, sigterm_handler);
void sigterm_handler(int arg)
{
_running = 0;
}
7)处理SIGCHLD信号
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN);
这样,内核在子进程结束时不会产生僵尸进程。
3、示例代码
sig_atomic_t:This is an integral type of an object that can be accessed as an atomic entity, even in the presence of asynchronous signals.
示例
#include <cstring>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
const int MAXFILE = 65535;
volatile sig_atomic_t _running = 1;
void sig_handler(int arg)
{
_running = 0;
}
int main()
{
pid_t pid;
int fd, len;
char *buf = "This is a Dameon\n";
len = strlen(buf);
pid = fork();
if (pid < 0)
{
cerr << "fork error!" << endl;
exit(1);
}
else if (pid > 0)
{
exit(0); //父进程中,则退出
}
//以下为在子进程中操作
cout << getpid() << endl;
setsid();
chdir("/home/edward/altraman/tmp");
umask(0);
for (int i = 0; i < MAXFILE; i++)
{
close(i);
}
signal(SIGTERM, sig_handler);
while(_running)
{
ofstream file_rw("/home/edward/altraman/tmp/demon.log", ios::out | ios::app);
if (!file_rw)
{
cerr << "open file failed" << endl;
exit(1);
}
file_rw.write(buf, len);
file_rw.close();
usleep(10 * 1000); //10毫秒
}
return 0;
}
参考
【1】 http://linux.die.net/man/2/setsid
【2】 http://baike.baidu.com/view/2124050.htm
【3】 http://baike.baidu.com/view/53123.htm
【4】 http://hi.baidu.com/skyforum/blog/item/c7ec25383f6131f3b211c7b0.html
【5】 UNIX环境高级编程
http://www.cppblog.com/tankzhouqiang/archive/2011/06/29/149778.aspx
【6】 http://www.tetx.com/program/htm/tetx/blog/view/blog_id/1291521391/index.htm