(7)处理SIGCHLD信号 。处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <fcntl.h> #include <syslog.h> #include <error.h> #include <sys/resource.h> void daemonize(const char*cmd){ int i,fd0,fd1,fd2; pid_t pid; struct rlimit rlt; struct sigaction act; act.sa_handler = SIG_IGN; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGHUP,&act,NULL); umask(0); if((pid = fork()) == -1){ perror("fork error"); exit(-1); }else if(pid > 0){ exit(0); } setsid(); chdir("/"); getrlimit(RLIMIT_NOFILE,&rlt); if(rlt.rlim_max == RLIM_INFINITY) rlt.rlim_max = 1024; for(i = 0 ; i < rlt.rlim_max ; i ++) close(i); fd0 = open("/dev/null",O_RDWR); fd1 = dup(0); fd2 = dup(0); openlog(cmd,LOG_CONS,LOG_DAEMON); if(fd0 != 0 || fd1 != 1 || fd2 != 2){ syslog(LOG_ERR,"unexpected file descriptors %d %d %d",fd0,fd1,fd2); exit(1); } } int main(){ daemonize("w"); sleep(50); exit(0); }
2、出错处理
守护进程没有控制终端,不能将错误写到标准输错上。大多数进程使用集中的守护进程出错syslog设施,该设施的接口是syslog函数
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
int setlogmask(int mask);
#include <stdarg.h>
void vsyslog(int priority, const char *format, va_list ap);
下面各种就看不懂了。
3、单实例守护进程
为了正常运作,某些守护进程实现为单实例,也就是在任一时刻只运行该守护进程的一个副本。文件锁和记录锁机制是这种方法的基础该方法用来保证一个守护进程只有一个副本在运行,如果每一个守护进程创建一个文件,并且在整个文件上加上一把写锁,那么就只允许创建一把这样的写锁,此后如试图再创建这样一把写锁就将失败,以此向后续守护进程副本指明已有一个副本正在运行。
#define LOCKFILE "/var/run/daemon.pid" #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) int lockfile(int fd){ struct flock fk; fk.l_type = F_WRLCK; fk.l_start = 0; fk.l_whence = SEEK_SET; fk.l_len = 0; return fcntl(fd,F_SETLK,&fk); } int already_running(){ int fd; char buf[16]; fd = open(LOCKFILE,O_RDWR | O_CREAT,LOCKMODE); if(fd < 0){ syslog(LOG_ERR,"Can't open %s: %s\n",LOCKFILE,strerror(errno)); exit(1); } if(lockfile(fd) < 0){ if(errno == EACCES || errno == EAGAIN){ close(fd); return 1; } syslog(LOG_ERR,"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; }1. 守护进程的每个副本都试图建立一个文件,并将进程ID写入该文件中。如果该文件已经加了锁,那么lockfile函数将失败,errno设置为EACCSS或EAGAIN,函数返回1,表明该进程已经运行在。
5、守护进程惯例
(1)若守护进程使用锁文件,那么该文件通常存放在/var/run目录中。
(2)若守护进程支持配置选项,那么配置文件通常存放在/etc目录中,配置文件的名字通常是name.conf,name就是该守护进程或服务的名字
(3)守护进程可以用命令行启动
(4)若一守护进程有一配置文件,那么当该守护进程启动时,它读该文件,但在此之后一般不会再查看它。若管理员更改了配置文件,那么该守护进程可能需要被重启,以使配置文件更改生效。为避免这种麻烦,某些守护进程将捕捉SIGHUP信号,当他们接收到该信号时,重新读取配置文件。因为守护进程不与终端相结合,或者是无控制终端的会话首进程,或者是孤儿进程组的成员,所以守护进程并不期望接受SIGHUP信号。于是,他们可以安全的重复使用它。
下面给出重读配置文件的程序:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <syslog.h> #include <sys/resource.h> #include <sys/stat.h> #include <signal.h> #include <pthread.h> #include <unistd.h> #define LOCKFILE "/home/xkey/APUE/13/daemon.pid" #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) sigset_t mask; int lockfile(int fd){ struct flock fk; fk.l_type = F_WRLCK; fk.l_start = 0; fk.l_whence = SEEK_SET; fk.l_len = 0; return fcntl(fd,F_SETLK,&fk); } int already_running(){ int fd; char buf[16]; fd = open(LOCKFILE,O_RDWR | O_CREAT,LOCKMODE); if(fd < 0){ syslog(LOG_ERR,"Can't open %s: %s\n",LOCKFILE,strerror(errno)); exit(1); } if(lockfile(fd) < 0){ if(errno == EACCES || errno == EAGAIN){ close(fd); return 1; } syslog(LOG_ERR,"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; } void daemonize(const char*cmd){ int i,fd0,fd1,fd2; pid_t pid; struct rlimit rlt; struct sigaction act; act.sa_handler = SIG_IGN; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGHUP,&act,NULL); umask(0); if((pid = fork()) == -1){ perror("fork error"); exit(-1); }else if(pid > 0){ exit(0); } setsid(); chdir("/"); getrlimit(RLIMIT_NOFILE,&rlt); if(rlt.rlim_max == RLIM_INFINITY) rlt.rlim_max = 1024; for(i = 0 ; i < rlt.rlim_max ; i ++) close(i); fd0 = open("/dev/null",O_RDWR); fd1 = dup(0); fd2 = dup(0); openlog(cmd,LOG_CONS,LOG_DAEMON); if(fd0 != 0 || fd1 != 1 || fd2 != 2){ syslog(LOG_ERR,"unexpected file descriptors %d %d %d",fd0,fd1,fd2); exit(1); } } void reread(){ puts("read daemo config file again."); } void* thread_func(void* arg){ //sigset_t *mask = (sigset_t*)arg; printf("mask %d\n",mask); int err,signo; while(1){ sigwait(&mask,&signo); switch(signo){ case SIGHUP: syslog(LOG_INFO,"Rereading configuration file.\n"); reread(); break; case SIGTERM: syslog(LOG_INFO,"Get SIGTERM;exiting.\n"); exit(0); default: syslog(LOG_INFO,"Unexpected signal %d.\n",signo); } } } void sighup(int signo){ printf("sighup %d\n",signo); reread(); } void sigterm(int signo){ printf("sigterm %d\n",signo); exit(0); } int main(int argc,char* argv[]){ pthread_t tid; struct sigaction act; char* cmd; if((cmd = strrchr(argv[0],'/'))==NULL) cmd = argv[0]; else cmd++; sleep(5); printf("%s\n",cmd); daemonize(cmd); if(already_running()){ syslog(LOG_ERR,"Daemon already running.\n"); exit(1); } //act.sa_handler = SIG_DFL;//多线程的方法 //act.sa_flags = 0; //sigemptyset(&act.sa_mask); //sigaction(SIGHUP,&act,NULL); //sigset_t mask; //sigfillset(&mask); //pthread_sigmask(SIG_BLOCK,&mask,NULL); //pthread_create(&tid,NULL,thread_func,0); // act.sa_handler = sighup;//简单信号 act.sa_flags = 0; sigaddset(&act.sa_mask,SIGTERM);//注意 sigaction(SIGHUP,&act,NULL); act.sa_handler = sigterm; act.sa_flags = 0; sigaddset(&act.sa_mask,SIGHUP);//注意 sigaction(SIGTERM,&act,NULL); sleep(60); exit(0); }