一、守护进程简介
守护进程是linux后台服务进程,它独立于终端(shell)并且周期性的执行某个特定任务。守护进程必须从当前的运行环境中脱离出来,当前环境通常是从父进程继承下来的,包括文件描述符、控制终端(shell)、工作目录、文件权限掩码、进程组、会话。守护进程的启动方式有三种:1、/etc/rc.d随系统启动 2、crond定时任务启动 3、终端(shell)启动。
实际上守护进程与其他进程一样,只是在编程时按照特定的步骤进行而已。
二、守护进程编写步骤
1、后台运行
首先创建子进程,父进程退出。此时程序给shell造成退出的假象,从而使子进程形式上脱离终端,用户可以在终端执行其他命令。程序的其他功能则在子进程中完成。
BTW:Linux中当父进程先于子进程退出时,子进程会变成孤儿进程,被系统的init(进程号为1)进程收养,此时子进程的父进程是init。
可以用如下代码实现:
-
if((pid = fork()) < 0) //1. fork , father process exit
-
{
-
perror("error fork");
-
exit(1);
-
}
-
else if(pid > 0) exit(0);
-
else {}
2、创建新会话,脱离控制终端、进程组、会话
首先要了解两个概念:进程组和会话期
进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号(PID)之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等 于进程组ID。
且该进程组ID不会因组长进程的退出而受到影响。
会话周期:会话期是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
此过程非常简单,调用 setsid()函数就可以完成。此处的setsid()函数作用有以下几点:
1、创建新会话并担任组长
2、使进程脱离原会话的控制
3、使进程脱离原进程组控制
4、使进程脱离原控制终端控制
由于在fork时,子进程继承了父进程的进程组、会话、控制终端,虽然父进程退出,但是这些信息没有改变,所以setsid使子进程完全独立出来,摆脱其他进程的控制。
3、完全脱离控制终端
此时进程成为会话组长,虽然是脱离了终端控制;但是还可以重新申请控制终端,所以需要进行限制,通过再创建子进程2使其不是会话组长,并且退出子进程1.
-
if((pid = fork()) < 0) //3
-
{
-
perror("error fork");
-
exit(1);
-
}
-
else if(pid > 0) exit(0);
-
else {}
4、改变当前工作目录
当前工作目录,如果有进程在活动的话,是无法卸载的。所以要改变工作目录,通常设置为根目录(/)。其他需要也可以改成其他目录。如日志(/tmp)
chdir("/");
5、修改文件权限掩码
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。
6、关闭文件描述符
子进程从父进程那里继承了文件描述符,但是子进程从不使用,这样就会造成资源浪费,而且文件系统无法卸载。所以应该关闭所有继承的文件描述符。
-
for(i = 0; i < getdtablesize(); i++) // 6
-
close(i);
7、信号处理
1、一般结束守护进程通过kill命令,所以程序需要响应kill发出的SIGTERM信号,来正常退出。
2、对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进 程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN);
这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。
三、实例
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <fcntl.h>
-
#include <unistd.h>
-
#include <sys/wait.h>
-
#include <signal.h>
-
-
void sigterm_handler(int arg);
-
volatile int _running = 1;
-
-
void init_daemon()
-
{
-
pid_t pid;
-
int i;
-
-
if((pid = fork()) < 0) //1. fork , father process exit
-
{
-
perror("error fork");
-
exit(1);
-
}
-
else if(pid > 0) exit(0);
-
else {}
-
-
setsid(); // 2
-
if((pid = fork()) < 0) //3
-
{
-
perror("error fork");
-
exit(1);
-
}
-
else if(pid > 0) exit(0);
-
else {}
-
-
chdir("/"); // 4
-
umask(0); // 5
-
-
for(i = 0; i < getdtablesize(); i++) // 6
-
close(i);
-
}
-
-
int main(void)
-
{
-
int i, fd, len;
-
char *buf = "this is a Dameon\n";
-
len = strlen(buf);
-
-
init_daemon();
-
signal(SIGTERM, sigterm_handler);
-
signal(SIGCHLD, SIG_IGN);
-
-
while( _running )
-
{
-
if((fd=open("/tmp/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600)) < 0)
-
{
-
perror("open");
-
exit(1);
-
}
-
write(fd, buf, len);
-
close(fd);
-
sleep(1); // 1s
-
}
-
}
-
-
void sigterm_handler(int arg)
-
{
-
_running = 0;
-
}
阅读(3248) | 评论(0) | 转发(0) |