彩世界平台-彩世界时时app-彩世界开奖app苹果下载

热门关键词: 彩世界平台,彩世界时时app,彩世界开奖app苹果下载

您的位置:彩世界平台 > 网站首页 > 守护进程之守护进程的惯例

守护进程之守护进程的惯例

发布时间:2019-09-05 03:20编辑:网站首页浏览(105)

    在UNIX系统中,守护进程遵循下列公共惯例:

        UNIX 系统中,守护进程遵循下列通用惯例。
        (1)若守护进程使用锁文件,则该文件通常存储在 /var/run 目录中。不过守护进程可能需要具有超级用户权限才能在此目录下创建文件。锁文件的名字通常是 name.pid,其中,name 是该守护进程或服务的名字。例如,cron 守护进程锁文件的名字是 /var/run/crond.pid。
        (2)若守护进程支持配置选项,则配置文件通常存放在 /etc 目录中,名字一般为 name.conf,其中,name 是该守护进程或服务的名字。例如,syslogd 守护进程的配置文件通常是 /etc/syslog.conf。
        (3)守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc* 或 /etc/init.d/*)启动的。如果要在守护进程终止时自动地重新启动它,可以在 /etc/inittab 中为该守护进程包括 respawn 记录项,这样 init 就将重新启动该守护进程。
        (4)守护进程的配置文件一般会在守护进程启动时读取(如果有的话),之后就不会再查看。若更改了配置文件,则该守护进程可能需要重启以使更改生效。为避免这种麻烦,某些守护进程会捕捉 SIGHUP 信号。当它们接收到该信号时,就重新读取配置文件。因为守护进程并不与终端相结合,它们或者是无控制终端的会话首进程,或者是孤儿进程组的成员,所以没有理由接收 SIGHUP,因而可以安全地重复使用 SIGHUP。
        此外,为了正常运作,某些守护进程在任一时刻只允许运行该守护进程的一个副本。例如,该守护进程可能需要排他地访问一个设备。这种情况可以采取文件和记录锁机制。这种方法保证一个守护进程只有一个副本在运行。如果每一个守护进程创建一个有固定名字的文件,并对该文件加一把写锁,那么只允许创建一把这样的写锁,之后创建写锁的尝试都会失败。而在该守护进程终止时,这把锁将被自动删除。
        下面这个函数演示了如何使用文件和记录锁来保证只允许一个守护进程的一个副本。其中的 lockfile 函数的实现见后面的fcntl 记录锁一节。

    • 若守护进程使用锁文件,那么该文件通常存放在/var/run目录中。注意,守护进程可能需要具有超级用户权限才能在此目录下创建文件。锁文件的名字通常是name.pid,其中,name是该守护进程或服务的名字。例如cron守护进程锁文件的名字是/var/run/crond.pid。
    • 若守护进程支持配置选项,那么配置文件通常存放在/etc目录中。配置文件的名字通常是name.conf,其中,name是该守护进程或服务的名字。例如,syslogd守护进程的配置文件是/etc/syslog.conf。
    • 守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc*或/etc/init.d/*)启动的。如果在守护进程终止时,应当自动地重新启动它,则我们可在/etc/inittab中为该守护进程包括_respawn记录项,这样,init就将重启动该守护进程。
    • 若一守护进程有一配置文件,那么当该守护进程启动时,它读该文件,但在此之后一般就不会再查看它。若一管理员更改了配置文件,那么该守护进程可能需要被停止,然后再启动,以使配置文件的更改生效。为避免此种麻烦,某些守护进程将捕捉SIGHUP信号,当它们接收到该信号时,重读配置文件。因为守护进程并不与终端相结合,它们或者是无控制终端的会话首进程,或者是孤儿进程组的成员,所以守护进程并不期望接收SIGHUP。于是,它们可以安全地重复使用它。
    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <unistd.h>
    
    #include <fcntl.h>
    
    #include <errno.h>
    
    #include <string.h>
    
    #include <syslog.h>
    
    #include <sys/stat.h>
    
    
    
    #define LOCKFILE "/var/run/singleDaemon.pid"
    
    #define LOCKMODE (S_IRUSR |S_IWUSR |S_IRGRP |S_IROTH)
    
    
    
    extern int lockfile(int);
    
    
    
    int already_running(void){
    
        int 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);
    
        char buf[16];
    
        sprintf(buf, "%ld", (long)getpid());
    
        write(fd, buf, strlen(buf)+1);
    
        return 0;
    
    }
    

    实例

        这里,如果操作的文件已经加了锁,那么 lockfile 函数就会失败,errno 会被设置为 EACCES 或 EAGAIN,表明该守护进程已在运行。否则将文件长度截断为 0,并写入进程 ID(将文件长度截断为 0 的原因是之前的守护进程的进程 ID 字符串可能长于当前的进程 ID 字符串。比如以前为 12345,现在的为 9999,那么写入后,文件中留下的将是 99995 而非 9999)。
        接下来的这个程序演示了守护进程在多线程中重读其配置文件的一种方法。其中用到了上面定义的这个 already_running 函数,而另一个函数 daemonize 的实现见守护进程编写规则与出错记录。

    程序清单13-3所示程序说明了守护进程可以重读其配置文件的一种方法。该程序使用sigwait以及多线程(可参考

    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <signal.h>
    
    #include <string.h>
    
    #include <syslog.h>
    
    #include <pthread.h>
    
    
    
    extern void daemonize(const char *);
    
    extern int already_running(void);
    
    
    
    sigset_t mask;
    
    
    
    void reread(void){
    
        /* read configuration... */
    
    }
    
    
    
    void *thr_fn(void *arg){
    
        int signo;
    
        for(;;){
    
            if(sigwait(&mask, &signo) != 0){
    
                syslog(LOG_ERR, "sigwait error");
    
                exit(1);
    
            }
    
            switch(signo){
    
            case SIGHUP:
    
                syslog(LOG_INFO, "reread configuration");
    
                reread();
    
                break;
    
            case SIGTERM:
    
                syslog(LOG_INFO, "got SIGTEM; thread exit");
    
                exit(0);
    
            default:
    
                syslog(LOG_INFO, "unexpected signal %dn", signo);
    
            }
    
        }
    
        return 0;
    
    }
    
    
    
    int main(int argc, char *argv[]){
    
        char *cmd = NULL;
    
        if((cmd = strrchr(argv[0], '/')) == NULL)
    
            cmd = argv[0];
    
        else
    
            cmd++;
    
        daemonize(cmd);         // become a daemon
    
        if(already_running()){  // ensure only one copy of the daemon is running.
    
            syslog(LOG_ERR, "daemon already running");
    
            exit(1);
    
        }
    
        struct sigaction act;
    
        act.sa_handler = SIG_DFL;   // restore SIGHUP default
    
        sigemptyset(&act.sa_mask);
    
        act.sa_flags = 0;
    
        if(sigaction(SIGHUP, &act, NULL) != 0){
    
            syslog(LOG_ERR, "can't restore SIGHUP default");
    
            exit(1);
    
        }
    
        sigfillset(&mask);          // and block all signals
    
        if(pthread_sigmask(SIG_BLOCK, &mask, NULL) != 0){
    
            syslog(LOG_ERR, "pthread_sigmask error");
    
            exit(1);
    
        }
    
        pthread_t tid;      // create a thread to handle SIGHUP and SIGTERM
    
        if(pthread_create(&tid, NULL, thr_fn, 0) != 0){
    
            syslog(LOG_ERR, "pthread_create errorn");
    
            exit(1);
    
        }
    
        /* preceed with the rest of the daemon */
    
        for(;;){
    
            pause();
    
        }
    
        exit(0);
    
    }
    

    程序清单13-3 守护进程重读配置文件

        这里要注意的是,在 daemonize 函数中修改了 SIGHUP 的默认处理行为,因此应该在调用之后恢复默认处理动作,否则调用 sigwait 的线程决不会见到该信号。如同对多线程程序所推荐的那样,这里阻塞了所有信号,并创建了单独的一个线程来负责处理 SIGHUP 和 SIGTERM 信号。因为 SIGHUP 和 SIGTERM 的默认动作是终止进程,所以在阻塞了这些信号后,当它们中的其中一个被发送到守护进程时,守护进程不会消亡。
        运行结果如下(省略了 grep 自身所在的行和一些不重要的列):

    #include <pthread.h><syslog.h>  already_running( * *= sigwait(&mask, &(err != ( argc,  *                *((cmd = strrchr(argv[], )) === argv[++
        =&= (sigaction(SIGHUP, &sa, NULL) < &((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != = pthread_create(&tid, NULL, thr_fn, (err != 
    
    $ sudo ./rereadConfig.out             # 需要 root 权限来运行
    
    $ ps -efj | grep rereadConfig.out     # 查看守护进程
    
    UID      PID     PPID  PGID    SID     TTY    CMD  
    
    root     109758  1     109757  109757  ?      ./rereadConfig.out
    
    $
    
    $ sudo kill -s SIGHUP 109758          # 发送 SIGHUP 信号来重读配置
    
    $ ps -efj | grep rereadConfig.out     # SIGHUP 信号不会终止守护进程
    
    UID      PID     PPID  PGID    SID     TTY    CMD  
    
    root     109758  1     109757  109757  ?      ./rereadConfig.out
    
    $ 
    
    $ sudo kill -s SIGTERM 109758         # 发送 SIGTERM 信号来重读配置
    
    $ ps -efj | grep rereadConfig.out     # SIGTERM 信号终止了守护进程
    
    $
    

    该程序调用

        当然,若要使用单线程守护进程来重读配置,可以在初始化守护进程后,分别为 SIGHUP 和 SIGTERM 配置信号处理程序,然后将重读逻辑放在信号处理程序中,也可以在信号处理程序中设置一个标志,并由守护进程的主线程来完成所有的工作。比如上面这个程序可以这样修改。

    如同对多线程程序所推荐的那样,我们阻塞所以信号,然后创建一线程,由它来处理信号。该线程的唯一工作是等待SIGHUP和SIGTERM。当接收到SIGHUP信号时,该线程调用reread函数重读它的配置文件。当它接收到SIGTERM信号时,记录一消息,然后终止。

    int main(){
    
        ...
    
        sa.sa_handler = sigterm;
    
        sigemptyset(&sa.sa_mask);
    
        sigaddset(&sa.sa_mask, SIGHUP);
    
        sa.sa_flags = 0;
    
        sigaction(SIGTERM, &sa, NULL);
    
        sa.sa_handler = sighup;
    
        sigemptyset(&sa.sa_mask);
    
        sigaddset(&sa.sa_mask, SIGTERM);
    
        sa.sa_flags = 0;
    
        sigaction(SIGHUP, &sa, NULL);
    
        ...
    
    }
    

    本文由彩世界平台发布于网站首页,转载请注明出处:守护进程之守护进程的惯例

    关键词:

上一篇:Ubuntu 8.04设置守护进程时出错的解决

下一篇:没有了