linux中Daemon守护进程编程
守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是通过守护进程实现的,同时,守护进程还能完成许多系统任务,例如,作业规划进程crond、打印进程lqd等(这里的结尾字母d就是Daemon的意思)。
一般情况下守护进程可以通过以下几种方式产生:
1,在系统启动的时候由启动脚本启动,这些启动脚本通常放在/etc/rc.d目录下
2,利用inetd超级系统服务器启动,如telnet等
3,由cron定时启动以及在终端用nohup启动的进程也是守护进程
由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能够突破这种限制,它从被执行开始运转,直到整个系统关闭时才退出。如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程。
守护进程的变成要点:
1. 蔽一些有关控制终端操作的信号
这是为了防止在守护进程还没悠运行之前,控制终端收到干扰推出或者挂起
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
2. 在后台运行。
为避免挂起控制终端而将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
if(pid=fork())
3. 脱离控制终端,登录会话和进程组
exit(0);//是父进程,结束父进程,子进程继续
进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
setsid()
4. 关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
#define NOFILE 256 ;//不同系统中不同数值
for(i=0;i<NOFILE;i++)
colse(i);
5. 改变当前工作目录
进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/")
chdir("/");
6. 重设文件创建掩模
进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除0:
umask(0);
7. 处理SIGCHLD(子进程退出信号)信号
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程 将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地 将SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN);
下面给出一个例程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
int init_daemon(const char *pname,int facility)
{
int pid;
int i;
/*忽略可能的终端信号*/
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
//创建子进程,父进程推出
if( (pid=fork()))
exit(EXIT_SUCCESS);//是父进程,结束父进程,子进程继续
else if(pid < 0)
{
perror("fork error!\n");
exit(EXIT_FAILURE);
}
setsid();//设置新会话组长,脱离终端
for(i=0;i<NOFILE;i++)
close(i);
open("/dev/null",O_RDONLY);
open("/dev/null",O_RDWR);
open("/dev/null",O_RDWR);
chdir("/");
umask(0);
signal(SIGCHLD,SIG_IGN);
openlog(pname,LOG_PID,facility);
return 0;
}
int main(int argc,char *argv[])
{
time_t ticks;
init_daemon(argv[0],LOG_KERN);
while(1)
{
sleep(1);
ticks = time(NULL); //读取当前时间
syslog(LOG_INFO,"%s",asctime(localtime(&ticks)));//写日志信息
}
return 0;
}
运行之后,使用 tail -n 1 /var/log/message
就可以查看到日志信息了。
(清悠我心:http://hi.baidu.com/%C7%E5%D3%C6%CE%D2%D0%C4/blog/category/linux%D3%A6%D3%C3%B1%E0%B3%CC)