1.守护进程最重要的特性是后台运行。
2.守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的。
总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别。
因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程(一个特殊的后台进程)。
lib_daemon.h文件
#ifndef __LIB_DAEMON_H__
#define __LIB_DAEMON_H__
/**
* init_daemon - 将进程变成守护进程
*
* @param path 要切换到的当前工作目录。参数为NULL是切换到/tmp
*
* @return 0 成功
* -1 fork失败
* -2 改变当前目录失败
*
* @note 返回失败时,标准输入输出不会关闭
**/
int init_daemon(char *path);
#endif
lib_daemon.cpp文件
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/param.h>
#include "lib_daemon.h"
int init_daemon(char *path)
{
int pid;
if ((pid = fork()) > 0)
{
exit(0); /* 是父进程,结束父进程 */
}
else if (pid < 0)
{
return -1; /* fork失败 */
}
/* 是第一子进程,继续 */
/* 如果该进程是一个进程组的组长,此函数返回错误。
为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行,
子进程继承了父进程的进程组ID,但是进程PID却是新分配的,所以不可能
是新会话的进程组的PID。从而保证了这一点。 */
setsid();
/* 创建一个新的会话 */
/* 第一子进程成为新的会话组长和进程组长 */
/* 脱离控制终端,登录会话和进程组 */
/* 现在,第一子进程已经成为无终端的会话组长。但它可以重新申请打开一个
控制终端。 可以通过使进程不再成为会话组长来禁止进程重新打开控制终端 */
if ((pid = fork()) > 0)
{
exit(0); /* 是第一子进程,结束第一子进程 */
}
else if (pid < 0)
{
return -1; /* fork失败 */
}
/* 是第二子进程,继续 */
/* 第二子进程不再是会话组长 */
/* 改变工作目录到/tmp */
/* 最好是切换到根目录,因为如果它的当前目录是在一个被安装的 文件系统上,那么就会妨碍这个文件系统
被卸载,非必须,防止mount出错 */
if (chdir(path ? path : "/tmp") != 0)
return -2;
/* parasoft suppress item BD-RES-INVFREE-1 */
/* 关闭标准输入输出,标准错误的文件描述符 */
close(STDIN_FILENO); /* parasoft-suppress BD-RES-INVFREE-1 */
close(STDOUT_FILENO); /* parasoft-suppress BD-RES-INVFREE-1 */
close(STDERR_FILENO); /* parasoft-suppress BD-RES-INVFREE-1 */
/* parasoft unsuppress item BD-RES-INVFREE-1 */
umask(0);
/* 进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件
的存取位,umask调用把守护进程的umask设置为0,这样取消了父进程的umask,避免了潜在的
干扰创建文件和目录 */
return 0;
}