zoukankan      html  css  js  c++  java
  • 守护进程之守护进程的惯例

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

    • 若守护进程使用锁文件,那么该文件通常存放在/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。于是,它们可以安全地重复使用它。

    实例

    程序清单13-3所示程序说明了守护进程可以重读其配置文件的一种方法。该程序使用sigwait以及多线程(可参考http://www.cnblogs.com/nufangrensheng/p/3540453.html)。

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

    #include "apue.h"
    #include <pthread.h>
    #include <syslog.h>
    
    sigset_t    mask;
    
    extern int already_running(void);
    
    void 
    reread(void)
    {
        /* ... */
    }
    
    void *
    thr_fn(void *arg)
    {
        int err, signo;
        for(;;)
        {
            err = sigwait(&mask, &signo);
            if(err != 0)
            {
                syslog(LOG_ERR, "sigwait failed");
                exit(1);
            }
        
            switch(signo)
            {
                case SIGHUP:
                    syslog(LOG_INFO, "Re-reading configuration file");
                    reread();
                    break;
            
                case SIGTERM:
                    syslog(LOG_INFO, "got SIGTERM; exiting");
                    exit(0);
    
                default:
                    syslog(LOG_INFO, "unexpected signal %d
    ", signo);
            }
        }
        return(0);
    }
    
    int
    main(int argc, char *argv[])
    {
        int                 err;
        pthread_t           tid;
        char                *cmd;
        struct sigaction    sa;
        
        if((cmd = strrchr(argv[0], '/')) == NULL)
            cmd = argv[0];
        else
            cmd++;
        
        /*
        * Become a daemon.
        */    
        daemonize(cmd);
    
        /*
        * Make sure only one copy of the daemon is running.
        */
        if(already_running())
        {    
            syslog(LOG_ERR, "daemon already running");
            exit(1);
        }
    
        /*
        * Restore SIGHUP default and block all signals.
        */
        sa.sa_handler = SIG_DFL;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        if(sigaction(SIGHUP, &sa, NULL) < 0)
            err_quit("%s: can't restore SIGHUP default");
        sigfillset(&mask);
        if((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0)
            err_exit(err, "SIG_BLOCK error");
    
        /*
        * Create a thread to handle SIGHUP and SIGTERM.
        */
        err = pthread_create(&tid, NULL, thr_fn, 0);
        if(err != 0)
            err_exit(err, "can't create thread");
        
        /*
        * Proceed with the rest of the daemon.
        */
        /* ... */
        exit(0);
    }

    该程序调用http://www.cnblogs.com/nufangrensheng/p/3544104.html中的daemonize以初始化守护进程。从该函数返回后,调用http://www.cnblogs.com/nufangrensheng/p/3544370.html中的already_running函数以确保该守护进程只有一个副本在运行。到达这一点时,SIGHUP信号仍被忽略(???),所以需要恢复对该信号的系统默认处理方式;否则调用sigwait的线程决不会见到该信号。

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

    回忆http://www.cnblogs.com/nufangrensheng/p/3514157.html中的表10-1,对于SIGHUP和SIGTERM的默认动作是终止进程。因为我们阻塞了这些信号,所以当对进程产生这些信号时,守护进程不会消亡,而是调用sigwait的线程在返回时将指示已接收到该信号。

    实例

    如在http://www.cnblogs.com/nufangrensheng/p/3540453.html中所说的那样,Linux线程对于信号的处理方式与众不同。由于这一点,在程序清单13-3中对信号标识合适的进程是困难的。另外,由于实现的差别,不能保证守护进程将按所期望的那样作出反应。

    程序清单13-4说明守护进程无需使用多线程也可以捕捉SIGHUP并重读其配置文件。

    #include "apue.h"
    #include <syslog.h>
    #include <errno.h>
    
    extern int lockfile(int);
    extern int already_running(void);
    
    void 
    reread(void)
    {
        /* ... */
    }
    
    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(int argc, char *argv[])
    {    
        char            *cmd;
        struct sigaction    sa;
        
        if((cmd = strrchr(argv[0], '/')) == NULL)
            cmd = argv[0];
        else
            cmd++;
    
        /*
        * Become a daemon.
        */
        daemonize(cmd);
    
        /*
        * Make sure only one copy of the daemon is running.
        */
        if(already_running())
        {
            syslog(LOG_ERR, "daemon already running");
            exit(1);
        }
        
        /*
        * Handle signals of interest.
        */
        sa.sa_handler = sigterm;
        sigempty(&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);
        }
        /*
        * Proceed with the rest of the daemon.
        */
        /* ... */
        exit(0);  
    }

    在初始化守护进程后,我们为SIGHUP和SIGTERM配置信号处理程序。我们可以将重读逻辑放在信号处理程序中,也可以只在其中设置一个标志,由守护进程的主线程做所有所需的工作。

    本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

  • 相关阅读:
    《神经网络和深度学习》系列文章三:sigmoid神经元
    《神经网络和深度学习》系列文章二:感知机
    《神经网络和深度学习》系列文章一:使用神经网络识别手写数字
    初遇python进程
    python-网络编程
    python常用模块详解2
    python根据正则表达式的简单爬虫
    python常用模块详解
    python-模块详解
    python-面向对象-内置方法补充
  • 原文地址:https://www.cnblogs.com/nufangrensheng/p/3544659.html
Copyright © 2011-2022 走看看