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、
客户机
/
服务器模型
•
守护进程通常充当一个服务器,等待客户端请求,并处理
•
后续章节会看到更多的双向通信的示例:
•
客户端发送请求,服务器处理并且,并发送响应结果