一、简介
守护进程,也就是所谓的“常驻程序”,是运行在后台的一种特殊进程。独立于控制终端且周期性执行某种任务或等待处理某些发生的事件。Linux大多数的服务器都是用守护进程来实现的。如Internet服务器inetd,Web服务器httpd等。
二、分类
服务器类型的守护进程基本上有两种类型:
1.总在运行的服务器
当操作系统启动时,这些服务器从/etc/rc等启动文件自动运行。有httpd,sendmail等。
2.只在需要时才运行的服务器
这些服务器是从inetd(xinetd)中启动。inetd超级服务器监听多个TCP/IP端口等待入境的请求,并且能够按需自动启动守护进程的灵活程序。
三、特点
1.后台运行(最重要特点)
2.必须与其运行前的环境隔离开来(这些环境包括未关闭的文件描述符,控制终端,回话,进程组,工作目录和文件创建掩模等)。
3.启动方式特殊。(可以从启动脚本/etc/rc.d启动,有作业规划进程crond启动,或者由用户在shell终端启动)
四、编程要点
编写守护进程实际上是把一个普通进程按照守护进程的特点改造成守护进程。Linux是基于System V的SVR4并遵循Posix
标准。编程要点如下:
1.后台运行
为了使守护进程进入后台执行,方法是在进程中调用fork创建出子进程,然后调用exit使父进程结束,让子进程进入后台执行。
if(pid = fork())
exit(0);
2.脱离控制终端,登录会话和进程组
Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号GID就是父进程的PID。登录会话可包含多个进程组,他们共享一个控制终端。这个控制终端通常是创建进程的登录终端。
控制终端,登录会话和进程组通常是从父进程继承下来的。要摆脱它们,使之不受影响的方法是在后台运行的基础上,调用setsid()使进程成为会话组长。
setsid();
3.禁止进程重新打开控制终端
进程已经成为了无终端的会话组长,但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端。
if(pid = fork())
exit(0);
4.关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符,不关闭会浪费资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。需要关闭他们。
for(i=0;i
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/param.h>
#include<sys/types.h>
#include<sys/stat.h>
void init_daemon(void)
{
int pid;
int i;
//1.创建子进程,父进程退出
if(pid=fork())
{
exit(0); //父进程退出
}
else if(pid < 0)
{
exit(1); //fork失败
}
//2.脱离控制终端,登录会话和进程组
setsid(); //第一子进程,成为新会话和父进程,与控制终端分离
//3.禁止进程重新打开终端,结束第一子进程
if(pid=fork())
{
exit(0); //结束第一子进程
}
else if(pid < 0)
{
exit(1);
}
//4.关闭打开的文件描述符
for(i=0;i< NOFILE;++i)
close(i);
//5.改变工作目录到/tmp
chdir("/tmp");
//6.重设文件创建掩模
umask(0);
return;
}
守护进程案例:
1.init_daemon.c
/** name : init_daemon
** function: 生成守护进程
*/
/****************STRAT**********************/
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/param.h>
#include<sys/types.h>
#include<sys/stat.h>
void init_daemon(void)
{
int pid;
int i;
//1.创建子进程,父进程退出
if(pid=fork())
{
exit(0); //父进程退出
}
else if(pid < 0)
{
exit(1); //fork失败
}
//2.脱离控制终端,登录会话和进程组
setsid(); //第一子进程,成为新会话和父进程,与控制终端分离
//3.禁止进程重新打开终端,结束第一子进程
if(pid=fork())
{
exit(0); //结束第一子进程
}
else if(pid < 0)
{
exit(1);
}
//4.关闭打开的文件描述符
for(i=0;i< NOFILE;++i)
close(i);
//5.改变工作目录到/tmp
chdir("/tmp");
//6.重设文件创建掩模
umask(0);
return;
}
/*******************END**************************/
2.test.c
/** name : test
** function: 创建主进程,调用init_daemon创建守护进程
** 实现5秒写一次日志的守护进程。^_^
*/
/****************STRAT**********************/
#include<stdio.h>
#include<time.h>
void init_daemon(void);
void main()
{
FILE *fp;
time_t t;
init_daemon();
while(1)
{
sleep(5);
if((fp=fopen("test.log","a"))>=0)
{
t=time(0);
fprintf(fp,"Im herea at %s by daemon.\n",asctime(localtime(&t)));
fclose(fp);
}
}
}
/*******************END**************************/
在Linux中编译,我所使用到的环境为Fedora 14 2.6内核。
结果如图,实现了每5秒向/tmp/test.log的守护进程。