守护进程编写的主要步骤如下:
1.屏蔽一些有关控制终端操作的信号。防止在守护进程没有正常运转起来时,控制终端受到干扰退出或挂起。
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP ,SIG_IGN);
2.将程序进入后台执行。由于守护进程最终脱离控制终端,到后台去运行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。这就是常说的“脱壳”。子进程继续函数fork()的定义如下:
pid_t fork(void);
3.脱离控制终端、登录会话和进程组。开发人员如果要摆脱它们,不受它们的影响,一般使用setsid()设置新会话的领头进程,并与原来的登录会话和进程组脱离。
4.禁止进程重新打开控制终端。
5.关闭打开的文件描述符,并重定向标准输入、标准输出和标准错误输出的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如果不关闭,将会浪费系统资源,引起无法预料的错误。关闭三者的代码如下:
for (fd = 0, fdtablesize = getdtablesize();
fd < fdtablesize; fd++)
close(fd);
6.改变工作目录到根目录或特定目录进程活动时,其工作目录所在的文件系统不能卸下
7.处理SIGCHLD信号。SIGCHLD信号是子进程结束时,向内核发送的信号。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。因此需要对SIGCHLD信号做出处理,回收僵尸进程的资源,避免造成不必要的资源浪费。可以用如下语句:
signal(SIGCHLD,(void *)reap_status);
捕捉信号SIGCHLD,用下面的函数进行处理:
void reap_status()
{ int pid;
….
}
程序:
init.c test.c
实验内容及步骤
守护进程实例包括两部分:主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。
1. init.c
#include < unistd.h >
#include < signal.h >
#include < sys/param.h >
#include < sys/types.h >
#include < sys/stat.h >
#include <stdlib.h>
void init_daemon(void)
{
int pid;
int i;
if(pid=fork())
exit(0);//是父进程,结束父进程
else if(pid< 0)
exit(1);//fork失败,退出
//是第一子进程,后台继续执行
setsid();//第一子进程成为新的会话组长和进程组长
//并与控制终端分离
if(pid=fork())
exit(0);//是第一子进程,结束第一子进程
else if(pid< 0)
exit(1);//fork失败,退出
//是第二子进程,继续
//第二子进程不再是会话组长
for(i=0;i< NOFILE;++i)//关闭打开的文件描述符
close(i);
chdir("/tmp");//改变工作目录到/tmp
umask(0);//重设文件创建掩模
return;
}
2. test.c
#include < stdio.h >
#include < time.h >
void init_daemon(void);//守护进程初始化函数
intmain() {
FILE *fp;
time_t t;
init_daemon();//初始化为Daemon
while(1)//每隔一分钟向test.log报告运行状态
{
sleep(60);//睡眠一分钟
if((fp=fopen("test.log","a")) >=0)
{
t=time(0);
fprintf(fp,"I'm here at %sn",asctime(localtime(&t)) );
fclose(fp);
}
}
}
编译: [root@vm root]#gcc –g –o test init.c test.c
执行: [root@vm root]#./test
查看进程: [root@vm root]#ps –ef
从输出可以发现test守护进程的各种特性满足上面的要求,这个问题掌握了,会在嵌入式开发中如虎添翼,加油吧。