1、守护进程的特征
•英文名称daemon,又称为常驻进程、精灵进程
•守护进程有以下特征:
•1、长时间后台运行,通常随系统启停而启停
•2、大多系统守护进程具有root权限
•3、没有控制终端
•4、所有用户级别(非内核级别)的守护进程都是进程组长、会话领导,
并且是进程组与会话中的唯一一组进程
•5、大多守护进程的父进程都是init进程
2、编码规则
•如果编码实现一个守护进程,需要考虑哪些因素:
•1、将文件创建掩码umask设置为0
•2、调用fork,然后父进程退出,目的:
shell能够接收下一条指令;
子进程不是进程组长,这是调用setsid函数的先决条件
•3、调用setsid创建一个新的会话
当前进程成为新会话的领导、成为新进程组的组长、没有控制终端
•4、将当前工作目录改变为根目录/
•5、关闭不再需要的文件描述符
•6、一些daemon进程将描述符0、1、2重定向为/dev/null设备
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
void z_daemon(void)
{
pid_t childpid = 0;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
/* 忽略信号 */
sa.sa_handler = SIG_IGN;
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
#ifdef SA_NOCLDSTOP
sa.sa_flags |= SA_NOCLDSTOP;
#endif
#ifdef SA_NOCLDWAIT
sa.sa_flags |= SA_NOCLDWAIT;
#endif
sigaction(SIGCHLD, &sa, NULL);
if( getppid() == 1 ) return;
fflush(stdout); fflush(stderr);
umask(0);
childpid = fork();
if( childpid < 0 ) exit(0);
else if( childpid > 0 ) exit(0);
/* the first child process */
if( setsid() == (pid_t) (-1) ) exit(0);
childpid = fork();
if( childpid < 0 ) exit(0);
else if( childpid > 0 ) exit(0);
/* the second child process */
return;
}
3、单实例守护进程
•即只能启动一个实例,不能多个实例同时运行
•如果做到这一点?
•文件与记录锁机制提供了一种简便的互斥机制
•一个进程放置“写”锁后,其它进程后续的加锁尝试都会失败
•另外,进程退出后,文件锁会自动释放,免除了手工清理的麻烦
•文件锁机制在下一章节详述,一个简单示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#define LOCKFILE "daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
int already_running(void)
{
int fd;
char buf[16];
fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
if( fd < 0 )
{
printf("can't open %s: %s\n", LOCKFILE, strerror(errno));
exit(1);
}
if( lockf(fd, F_TLOCK, 0) < 0 )
{
if( errno == EACCES || errno == EAGAIN )
{
close(fd);
return(1);
}
printf("can't lock %s: %s\n", LOCKFILE, strerror(errno));
exit(1);
}
ftruncate(fd, 0);
sprintf(buf, "%ld", (long)getpid());
write(fd, buf, strlen(buf) + 1);
return(0);
}
int main(void)
{
if( already_running() )
{
printf("already running ...\n");
exit(0);
}
printf("ok\n");
sleep(100);
}
4、守护进程的惯例
•系统的守护进程在收到SIGHUP信号后,会重新读取配置文件
5、客户机/服务器模型
•守护进程通常充当一个服务器,等待客户端请求,并处理
•后续章节会看到更多的双向通信的示例:
•客户端发送请求,服务器处理并且,并发送响应结果