守护进程(Daemon)是运行在后台种特殊进程它独立于控制终端并且周期性地执行某种任务或等待处理某些发生事件守护进程是种很有用进程Linux大多数就是用守护进程实现比如InternetinetdWeb服务器httpd等同时守护进程完成许多系统任务比如作业规划进程crond打印进程lpd等
守护进程编程本身并不复杂复杂是各种版本Unix实现机制不尽相同造成区别Unix环境下守护进程编程规则并不致这需要读者注意照搬某些书上规则(特别是BSD4.3和低版本 V)到Linux会出现下面将全面介绍Linux下守护进程编程要点并给出详细例子
. 守护进程及其特性
守护进程最重要特性是后台运行在这点上DOS下常驻内存TSR和的相似其次守护进程必须和其运行前环境隔离开来这些环境包括未关闭文件描述符控制终端会话和进程组工作目录以及文件创建掩模等这些环境通常是守护进程从执行它父进程(特别是shell)中继承下来最后守护进程启动方式有其特殊的处它可以在Linux系统启动时从启动脚本/etc/rc.d中启动可以由作业规划进程crond启动还可以由用户终端(通常是shell)执行
总的除开这些特殊性以外守护进程和普通进程基本上没有什么区别因此编写守护进程实际上是把个普通进程按照上述守护进程特性改造成为守护进程如果读者对进程有比较深入认识就更容易理解和编程了
2. 守护进程编程要点
前面讲过区别Unix环境下守护进程编程规则并不致所幸是守护进程编程原则其实都样区别在于具体实现细节区别这个原则就是要满足守护进程特性同时Linux是基于Syetem VSVR4并遵循Posix标准实现起来和BSD4相比更方便编程要点如下;
1. 在后台运行
为避免挂起控制终端将Daemon放入后台执行思路方法是在进程中fork使父进程终止让Daemon在子进程中后台执行
(pid=fork)
exit(0);//是父进程结束父进程子进程继续
2. 脱离控制终端登录会话和进程组
有必要先介绍下Linux中进程和控制终端登录会话和进程组的间关系:进程属于个进程组进程组号(GID)就是进程组长进程号(PID)登录会话可以包含多个进程组这些进程组共享个控制终端这个控制终端通常是创建进程登录终端
控制终端登录会话和进程组通常是从父进程继承下来我们目就是要摆脱它们使的不受它们影响思路方法是在第1点基础上sid使进程成为会话组长:
sid;
介绍说明:当进程是会话组长时sid失败但第点已经保证进程不是会话组长sid成功后进程成为新会话组长和新进程组长并和原来登录会话和进程组脱离由于会话过程对控制终端独占性进程同时和控制终端脱离
3. 禁止进程重新打开控制终端
现在进程已经成为无终端会话组长但它可以重新申请打开个控制终端可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:
(pid=fork)
exit(0);//结束第子进程第 2子进程继续(第 2子进程不再是会话组长)
4. 关闭打开文件描述符
进程从创建它父进程那里继承了打开文件描述符如不关闭将会浪费系统资源造成进程所在文件系统无法卸下以及引起无法预料按如下思路方法关闭它们:
for(i=0;i 关闭打开文件描述符close(i);>
5. 改变当前工作目录
进程活动时其工作目录所在文件系统不能卸下般需要将工作目录改变到根目录对于需要转储核心写运行日志进程将工作目录改变到特定目录如/tmpchdir("/")
6. 重设文件创建掩模
进程从创建它父进程那里继承了文件创建掩模它可能修改守护进程所创建文件存取位为防止这点将文件创建掩模清除:umask(0);
7. 处理SIGCHLD信号
处理SIGCHLD信号并不是必须但对于某些进程特别是服务器进程往往在请求到来时生成子进程处理请求如果父进程不等待子进程结束子进程将成为僵尸进程(zombie)从而占用系统资源如果父进程等待子进程结束将增加父进程负担影响服务器进程并发性能在Linux下可以简单地将SIGCHLD信号操作设为SIG_IGN
signal(SIGCHLD,SIG_IGN);
这样内核在子进程结束时不会产生僵尸进程这点和BSD4区别BSD4下必须显式等待子进程结束才能释放僵尸进程
3. 守护进程例子
守护进程例子包括两部分:主test.c和化init.c主每隔分钟向/tmp目录中日志test.log报告运行状态化中init_daemon负责生成守护进程读者可以利用init_daemon生成自己守护进程
1. init.c清单
# < unistd.h >
# < signal.h >
# < sys/param.h >
# < sys/types.h >
# < sys/stat.h >
void init_daemon(void)
{
pid;
i;
(pid=fork)
exit(0);//是父进程结束父进程
(pid< 0)
exit(1);//fork失败退出
//是第子进程后台继续执行
sid;//第子进程成为新会话组长和进程组长
//并和控制终端分离
(pid=fork)
exit(0);//是第子进程结束第子进程
(pid< 0)
exit(1);//fork失败退出
//是第 2子进程继续
//第 2子进程不再是会话组长
for(i=0;i< NOFILE;i)//关闭打开文件描述符
close(i);
chdir("/tmp");//改变工作目录到/tmp
umask(0);//重设文件创建掩模
;
}
2. test.c清单
# < stdio.h >
# < time.h >
void init_daemon(void);//守护进程化
{
FILE *fp;
time_t t;
init_daemon;//化为Daemon
while(1)//每隔分钟向test.log报告运行状态
{
sleep(60);//睡眠分钟
((fp=fopen("test.log","a")) >=0)
{
t=time(0);
fprf(fp,"Im here at %s\n",asctime(localtime(&t)) );
fclose(fp);
}
}
}
以上在RedHat Linux6.0下编译通过步骤如下:
编译:gcc -g -o test init.c test.c
执行:./test
查看进程:ps -ef
从输出可以发现test守护进程各种特性满足上面要求
最新评论