#include<stdio.h>
#include<signal.h>
#include<syslog.h>
#include<unistd.h>
#include<fcntl.h>
#include<time.h>
#include<sys/file.h>
#include<sys/ioctl.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
time_t now;
int childpid,fd,fdtablesize;
// int error,in,out;
int fp;
//屏蔽一些有关控制终端操作的信号。防止在守护进程没有正常运转起来时,控制终端受到干扰退出或挂起,此处忽略了终端I/O信号、STOP信号
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
//由于子进程会继承父进程的某些特性,如控制终端、登录会话、进程组等,而守护进程最终要脱离控制终端到后台去运行,所以必须把父进程杀掉,以确保子进程不是进程组长,这也是成功调用setsid()所要求的。
if (fork() != 0)
{
exit(1);
}
//脱离了控制终端还要脱离登录会话和进程组,这里可以调用setsid()函数,调用成功后程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离,由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
/*
if (setsid() < 0)
{
exit(1);
}
*/
//要达到setsid()函数的功能也可以用如下处理方法。"/dev/tty"是一个流设备,也是终端映射,调用close()函数将终端关闭。
if ((fp=open("/dev/tty",O_RDWR)) >= 0)
{
ioctl(fp,TIOCNOTTY,NULL);
close(fp);
}
//进程已经成为无终端的会话组长,但它可以重新申请打开一个新的控制终端。可以通过不再让进程成为会话组长的方式来禁止进程重新打开控制终端,需要再次调用fork函数。
if (fork() != 0)
{
exit(1);
}
//从父进程继承过来的当前工作目录可能在一个装配的文件系统中。因为守护进程通常在系统重启之前是一直存在的,所以如果守护进程的当前工作目录在一个装配文件系统中,那么该文件系统就不能被卸载。比如说从父进程继承的当前目录是/mnt下面的一个被挂载的目录。
if (chdir("/tmp") == -1)
{
exit(1);
}
//关闭打开的文件描述符,或重定向标准输入、标准输出和标准错误输出的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符。如果不关闭,将会浪费系统资源,引起无法预料的错误。getdtablesize()返回某个进程所能打开的最大的文件数。
for (fd=0,fdtablesize=getdtablesize();fd<fdtablesize;fd++)
{
close(fd);
}
//有的程序有些特殊的需求,还需要将这三者重新定向。
/*
error=open("/tmp/error",O_WRONLY|O_CREAT,0600);
dup2(error,2);
close(error);
in=open("/tmp/in",O_RDONLY|O_CREAT,0600);
if(dup2(in,0)==-1) perror("in");
close(in);
out=open("/tmp/out",O_WRONLY|O_CREAT,0600);
if(dup2(out,1)==-1) perror("out");
close(out);
*/
//由继承得来的文件方式创建的屏蔽字可能会拒绝设置某些权限,所以要重新赋于所有权限。
umask(0);
//如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源,如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。因此需要对SIGCHLD信号做出处理,回收僵尸进程的资源,避免造成不必要的资源浪费。
signal(SIGCHLD,SIG_IGN);
//守护进程不属于任何终端,所以当需要输出某些信息时,它无法像一般程序那样将信息直接输出到终端,可以使用linux中自带的syslogd守护进程,它向用户提供了syslog()系统调用函数。信息都保存在/var/log/syslog文件中。
syslog(LOG_USER|LOG_INFO,"守护进程测试!\n");
//每隔60秒钟就向/var/log/syslog日志文件里写入一次syslog()发出的信息。
while (1)
{
//time(time_t *t)返回从1970年1月1日0时0分0秒到现在的所有秒数。
time(&now);
//获得父进程ID和子进程ID。
syslog(LOG_USER|LOG_INFO,"PPID: %d PID: %d\n",getppid(),getpid());
//ctime(const time_t *)返回当前时间的一个char型指针。
syslog(LOG_USER|LOG_INFO,"当前时间:%s\n",ctime(&now));
//睡眠60秒
sleep(60);
}
return 0;
}
|