守护进程

简介

守护线程在系统启动时运行,在系统终止时退出,没有控制终端,只在后台作为一个服务默默运行

编写规则

1.调用umask()设置文件创建时的权限规则
2.调用fork, 然后使父进程exit
3.调用setsid创建一个新会话
4.将系统根目录设置为当前工作目录
5.关闭不再需要的文件描述符
6.将0,1,2标准输入输出重定向到/dev/null

出错记录

void openlog(const char *ident, int option, int facility)

ident会被记录在日志内, 作为与其它进程记录的区分

void syslog(int priority, const char *format, ...);

priority表示等级, 如消息/警告/出错等;
第二个参数表示可以进行格式化, 所有%m字符被会被自动替换成strerror(errno)表示的字符串

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/resource.h>
 
void daemonize(const char *cmd){
    int i,fd0,fd1,fd2;
    pid_t pid;
    struct rlimit rl;
    struct sigaction sa;
 
    umask(0);
 
    if(getrlimit(RLIMIT_NOFILE,&rl) < 0){
        perror("getrlimit failed");
        exit(1);
    }
 
    if((pid=fork()) <0 ){
        perror("fork failed");
        exit(1);
    }else if(pid != 0)
        exit(0);
    setsid();
 
    sa.sa_handler=SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags=0;
    if(sigaction(SIGHUP,&sa,NULL) <0){
        perror("sigaction failed");
        exit(1);
    }
    if((pid=fork())<0){
        perror("fork failed");
        exit(1);
    }else if(pid != 0)
        exit(0);
 
    if(chdir("/")<0){
        perror("chdir error");
        exit(1);
    }
 //关闭所有打开的文件描述符, 包括stdin/stdout/stderr
    if(rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max=1024;
    for(i=0;i<rl.rlim_max;i++)
        close(i);
 //由于上面已经关闭所有文件描述符, 所以新建文件描述符时会从0开始计数, 于是fd0=0,依次fd1=1,fd2=2
    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 description %d %d %d", fd0, fd1, fd2);
        exit(1);
    }
}
 
int main(){
   daemonize("io.c");
   return 0; 

单例模式

单例模式: 一个软件在系统只能运行一个实例, 不能同时运行两个或多个
方法: 创建一个文件并加锁, 第一个实例运行时没问题, 第二实例运行时会因为对文件加锁失败而退出

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
 
#define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
 
int lockfile(int fd){
    struct flock fl;
 
    fl.l_type=F_WRLCK;
    fl.l_start=0;
    fl.l_whence=SEEK_SET;
    fl.l_len=0;
    return(fcntl(fd,F_SETLK,&fl));
}
 
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",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",LOCKFILE,strerror(errno));
        exit(1);
    }
    ftruncate(fd,0);
    sprintf(buf,"%ld",(long)getpid());
    write(fd,buf,strlen(buf)+1);
    return 0;
}
 
int main(){
    int ret;
    ret=already_running();
//第二个实例会打印already running,然后退出
    if(ret == 1){
        printf("already running 
");
        exit(0);
    }
//第一个实例一直运行
    while(1){
        printf("from %ld 
",(long)getpid());
        sleep(2);
    }
    return 0;
}

单线程单例守护进程

如果守护线程的文件是锁文件, 一般存储在/var/run目录, 名称的形式多为name.pid
如果文件是配置文件, 一般存储在/etc目录, 名称的形式为name.conf
当配置文件修改后, 需手动发送sighup信号给后台进程: kill -SIGHUP xxx

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/resource.h>
 
#define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
void daemonize(const char *cmd){ //同上
    ... 
}
 
int lockfile(int fd){ //同上
    ...
}
 
int already_running(){ //同上
   ...
}
 
void reread(){
    int fd;
    fd=open(LOCKFILE,O_RDONLY);
    if(fd<0){
        syslog(LOG_ERR,"can't open %s: %s",LOCKFILE,strerror(errno));
        exit(1);
    }
    char buf[100]={0};
    int nread;
    if((nread=read(fd,buf,100))>0)
        syslog(LOG_INFO,"file content change to %s",buf);
}
void sigterm(int signo){
    syslog(LOG_INFO,"got SIGTERM; exiting");
    exit(0);
}
void sighup(int signo){
    syslog(LOG_INFO,"Re-reading configuration file");
    reread();
}
int main(){
    char *cmd="daemonize test";
    daemonize(cmd);
 
    if(already_running()){
        syslog(LOG_ERR,"daemon already running");
        exit(1);
    }
 
    struct sigaction sa;
    sa.sa_handler=sigterm;
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask,SIGHUP);
    sa.sa_flags=0;
    if(sigaction(SIGTERM,&sa,NULL)<0){
        syslog(LOG_ERR,"can't catch SIGTERM: %s",strerror(errno));
        exit(1);
    }
    sa.sa_handler=sighup;
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask,SIGTERM);
    sa.sa_flags=0;
    if(sigaction(SIGHUP,&sa,NULL)<0){
        syslog(LOG_ERR,"can't catch SIGHUP: %s",strerror(errno));
        exit(1);
    }
 
//业务写这里
    while(1){
        sleep(5);
    }
    exit(0);
}
原文地址:https://www.cnblogs.com/cfans1993/p/5643357.html