有许多人可能以为直接fork个子进程把父进程结束了就可以生成一个守护进程,其实里面还有很多问题,下面的代码是依照APUE中的代码修改的。
注解中有很多自己遇到的关于SIGHUP信号处理,和setsid后为什么还要fork的理解。
头文件:
-
#ifndef __unix_test__daemon__
-
#define __unix_test__daemon__
-
-
-
extern void daemonize(void(*)(int));
-
extern int already_open(int);
-
extern int my_writelog(const char* pname,const char* str) ;
-
extern int create_daemon(int (*daemon_handler)() , void (*sighup_handler)(int));
-
#endif /* defined(__unix_test__daemon__) */
实现:
-
#include "daemon.h"
-
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <pthread.h>
-
#include <fcntl.h>
-
#include <stdlib.h>
-
#include <sys/resource.h>
-
#include <signal.h>
-
#include <sys/signal.h>
-
#include <sys/stat.h>
-
#include <string.h>
-
#include <syslog.h>
-
#include <sys/file.h>
-
-
//守护进程注销
-
int daemondestroy(){
-
return 0;
-
}
-
//重新加载配置文件
-
int reread(){
-
return 0;
-
}
-
//get signal thread
-
//argument : ptr is wait signal set.
-
void * get_signal(void*ptr) {
-
sigset_t * wt = (sigset_t*)ptr;
-
int signo = 0 ;
-
int ret = 0;
-
-
//wait signal is empty , process exit.(not thread exit)
-
if (!ptr || !(*wt) ) {
-
exit(0);
-
}
-
sigaddset(wt, SIGQUIT);
-
printf("wt %u\n",*wt);
-
while (1) {
-
sigwait(wt, &signo);
-
switch (signo) {
-
case SIGHUP:
-
ret = reread();
-
if (ret) {
-
ret = my_writelog("unix_test","reread error");
-
}
-
break;
-
case SIGQUIT:
-
ret = daemondestroy();
-
if (ret) {
-
ret = my_writelog("unix_test","daemon quit error");
-
}
-
exit(0);
-
break;
-
default:
-
break;
-
}
-
}
-
return (void*)5;
-
}
-
//write system log
-
//return zero is success.
-
int my_writelog(const char* pname,const char* str) {
-
if (!pname || !str) {
-
return 1;
-
}
-
openlog(pname,LOG_PID ,LOG_USER);
-
syslog(LOG_ERR, "info:{ %s %m.} \n",str);
-
closelog();
-
return 0;
-
}
-
//【daemon程序】守护进程
-
//功能:
-
//主线程:
-
// 【阻塞】所有信号
-
// 循环写test1.txt文件10000次
-
//次线程:
-
// 【开启】SIGHUP and SIGQUIT
-
// 开辟另一个线程tid来处理信号,如果是SIGHUP就重新读取守护进程,SIGQUIT就退出守护进程
-
-
int create_daemon(int (*daemon_handler)() , void (*sighup_handler)(int)){
-
int fd = 0;
-
pthread_t tid;
-
sigset_t wt,set,save;
-
int ret = 0;
-
-
//NOTE:
-
//SIGHUP is sighup_handler , close all file descriptor
-
//1. don't used already file descriptor.
-
//2. SIGHUP signal is not default.
-
daemonize(sighup_handler);
-
-
-
fd = open("./lock.txt",O_RDWR|O_CREAT, \
-
((S_IRUSR)|(S_IWUSR)|(S_IRGRP)|(S_IROTH)));
-
if (fd < 0) {
-
char buf[1024] ={0};
-
sprintf(buf, "don't open file %5d ",fd);
-
my_writelog("unix_test", buf);
-
exit(0);
-
}
-
-
if (already_open(fd)) {
-
my_writelog("unix_test", "don't second daemon");
-
exit(0);
-
}
-
-
-
sigfillset(&set);
-
sigemptyset(&wt);
-
sigemptyset(&save);
-
//daemon wait signal set.
-
sigaddset(&wt, SIGQUIT);
-
sigaddset(&wt, SIGHUP);
-
-
ret = pthread_sigmask(SIG_BLOCK, &set, &save);
-
if (ret) {
-
return 1;
-
}
-
-
ret = pthread_create(&tid, NULL, get_signal, &wt);
-
if (ret) {
-
return 1;
-
}
-
-
ret = daemon_handler();
-
-
-
if (ret) {
-
return 1;
-
}
-
-
//daemon exit clean.
-
-
//igrone return value.
-
pthread_sigmask(SIG_SETMASK, &save, NULL);
-
-
ret = pthread_cancel(tid);
-
if (ret) {
-
return 1;
-
}
-
//igrone return value.
-
pthread_join(tid, (void*)&wt);
-
close(fd);
-
return 0;
-
}
-
-
-
//return is success lock file, reutnr other file is locked.
-
int already_open(int fd){
-
if (fd > 0) {
-
int m = flock(fd, LOCK_EX|LOCK_NB);
-
if ( m ) {
-
my_writelog("unix_test", "file is locked");
-
}else {
-
return 0;
-
}
-
}else {
-
my_writelog("unix_test", "file is open error");
-
}
-
return 1;
-
}
-
-
-
//让进程变成守护进程函数
-
//当进程调用daemonize,此进程变成【守护进程】
-
//1.修改文件屏蔽属性【防止改文件时候有屏蔽字不成功】创建文件rw-r--r--
-
//2.把SIGHUP信号重定向到一个空函数,防止父进程退出,子进程收到SIGHUP
-
// 【当父进程退出后子进程组变成孤儿进程组将收到SIGHUP和SIGCONT信号】,让子进程退出。
-
//3.进程1 fork 进程2
-
//4.进程2 setsid,setsid后进程成session leader 进程组长,没有控制终端
-
//5.进程2 fork 进程3【守护进程】,
-
// 此时为什么还要fork 进程3,因为进程2是session leader,是进程组组长可能还会获得控制终端
-
//6.进程3【守护进程】是一个没有控制终端,也不是进程组长的进程【孤儿进程组中的孤儿进程】
-
//7.进程3符合所有守护进程的条件,所以进程3才是最后的守护进程
-
//8.关闭文件描述符【防止程序和终端连接】,写或读终端
-
void daemonize(void (*sighup_handler)(int)){
-
int fd = 0 ;
-
int i = 0 ;
-
pid_t pid;
-
struct sigaction sa;
-
struct rlimit r;
-
-
umask(0);
-
-
sa.sa_flags = 0;
-
sigemptyset(&sa.sa_mask);
-
//这里忽略SIGHUP是防止进程2退出后给我们的守护进程发送SIGHUP信号
-
//但是这里如果忽略信号则在以后的sigwait中会丢失这个信号所以用一个空函数来处理这个信号
-
//sa.__sigaction_u.__sa_handler = SIG_IGN;
-
sa.__sigaction_u.__sa_handler = sighup_handler;
-
sigaction(SIGHUP, &sa, NULL);
-
-
if ( 0 > (pid = fork())) {
-
exit(0);
-
}else if (0 != pid) {
-
exit(0);
-
}
-
-
//if process isn't session leader
-
//1).become sessoin leader
-
//2).become team leader of new process group .
-
//3).terminate controlling terminal of the process.
-
setsid();
-
if ( 0 > (pid = fork())) {
-
exit(0);
-
}else if (0 != pid) {
-
exit(0);
-
}
-
getrlimit(RLIMIT_NOFILE, &r);
-
-
//close standard input/output(close controlling terminal) and other files.
-
for (i = 0 ; i < r.rlim_cur; ++i) {
-
close(i);
-
}
-
i = 0;
-
//assign input/output/errout to /dev/null
-
fd = open("/dev/null", O_RDWR);
-
fd = dup(0);
-
fd = dup(0);
-
}
阅读(2633) | 评论(0) | 转发(0) |