一、什么是守护进程
守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。
Linux 的大多数服务器就是用守护进程实现的。比如,Internet 服务器 inetd,Web 服务器 httpd 等。
二、如何查看守护进程
在终端敲:ps ajx
- a 表示不仅列当前用户的进程,也列出所有其他用户的进程;
- x 表示不仅列有控制终端的进程,也列出所有无控制终端的进程;
- j 表示列出与作业控制相关的信息。
守护进程的特点为: - 守护进程基本上都是以超级用户启动;
- 终端进程组ID为-1;
- 没有控制终端。
三、创建守护进程的步骤
创建一个守护进程一般有如下几步:
1. 调用umask(0),将文件模式创建屏蔽字改为0;
2. 调用fork(),父进程退出 (原因和目的:1、父进程终止可以让shell切换到前台继续等待用户输入命令;2、保证子进程不是一个进程组的组长进程);
3. 在子进程中调用 setsid() (原因和目的:让调用进程成为新会话的首进程,成为一个进程组的组长进程,并且脱离终端,即没有控制终端);
4. 调用chdir("/")将当前工作目录更改为根目录 (原因和目的:防止用户改动目录,从而影响进程的运行,例如:用户有时候需要挂载一个系统在某个目录下,如果这个目录有守护进程,则这个系统无法卸载);
5. 关闭相应的文件描述符(原因和目的:当前进程已经脱离终端,防止产生并不需要的交互作用,还有一个原因,用fork新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,所以需要关闭);
6. 忽略SIGCHLD信号 (原因和目的子进程退出的时候资源能够被系统所回收,防止出现僵尸进程 )。
还有其他的一些辅助步骤,例如:
a. 屏蔽一些控制终端操作的信号 :
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP ,SIG_IGN);
b. fork 两次:
// fork一次
if( pid=fork() ){ // 父进程
exit(0); //结束父进程,子进程继续
}else if(pid< 0){ // 出错
perror("fork");
exit(EXIT_FAILURE);
}
// fork两次
if( pid=fork() ){ // 父进程
exit(0); // 结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
}else if(pid< 0){ // 出错
perror("fork");
exit(EXIT_FAILURE);
}
四、demo
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
void create_daemon()
{
// 1、 将文件模式创建屏蔽字设置为0
umask(0);
// 2、 调用fork 父进程退出
pid_t pid = 0;
if ((pid = fork()) < 0)
{
perror("fork");
exit(2);
}
if (pid > 0)
{ // father
exit(3);
}
// 3、调用setsid 创建一个新的会话
setsid();
// 4、将当前工作目录改为更目录
chdir("/");
// 5、关闭不需要的文件描述符
// NOFILE 为 <sys/param.h> 的宏定义
// NOFILE 为文件描述符最大个数,不同系统有不同限制
for(i=0; i < NOFILE; ++i)
{
close(i);
}
// 6、忽略SIGCHLD 信号
signal(SIGCHLD, SIG_IGN);
// 守护进程逻辑
while(1)
{
// Do something
}
}
int main()
{
create_daemon();
return 0;
}