UNIX高级环境编程读书笔记(chapter13)

来源:互联网 发布:携程亲子园 知乎 编辑:程序博客网 时间:2024/06/09 23:04

第十三章    守护进程



        守护进程也称精灵进程是生存期较长的一种进程,它们常常在系统自举时启动,关闭时才终止,因为它们没有控制终端,所以说它们是在后台运行的。

        系统进程依赖于操作系统实现。父进程ID为0的进程通常是内核进程,它们作为系统自举过程的一部分而启动。

一、编程规则

       在编写守护进程程序时需遵循一些基本规则,以便防止产生并不需要的交互作用:

       (1)首先要做的是调用umask将文件模式创建屏蔽字设置为0。由继承得来的文件模式创建屏蔽字可能会拒绝设置          某些权限。

       (2)调用fork,然后使父进程退出。

       (3)调用setsid以创建一个新会话。

       (4)将当前工作目录更改为新目录。

       (5)关闭不在需要的文件描述符。

       (6)


实例:初始化一个守护进程

#include "apue.h"#include <syslog.h>#include <fcntl.h>#include <sys/resource.h>voiddaemonize(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)    {        err_quit("%s: can't get file limit", cmd);    }    if ((pid == fork()) < 0)    {        err_quit("%s: can't fork",cmd);    }    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)    {        err_quit("%s: can't ignore SIGHUP");    }    if ((pid = fork()) < 0)    {        err_quit("%s: can't fork", cmd);    }    else if (pid != 0)    {        exit(0);    }    if (rl.rlim_max == RLIM_INFINITY)    {        rl.rlim_max = 1024;    }    for (i = 0; i < rl.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);    }}


二、出错记录

        有三种方法产生日志消息:

        (1)内核例程可以调用log函数。

        (2)大多数用户进程(守护进程)调用syslog(3)函数以产生日志信息。

        (3)在此主机上的一个用户进程,或通过TCP/IP网络链接到此主机的其它主机上的一个用户进程可将日志消息发向UDP端口514.

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

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

         void closelog(void);

         int setlogmask(int maskpri);

       调用openlog是可选择的。如果不调用openlog,则在第一次调用syslog时, 自动调用openlog。调用closelog也是可选择的——它只是关闭曾被用于与syslog守护进程通信的描述符。

       调用syslog产生一个日志消息。

       setlogmask函数用于设置进程的记录优先级屏蔽字。

三、单实例守护进程

       为了正常运作,某些守护进程实现为单实例的,也就是在任一时刻只运行该守护进程的一个副本。

       文件锁和记录锁机制是一种方法的基础,该方法用来保护一个守护进程只有一个副本在运行。

       文件锁个记录锁提供了一种方便的互斥机制。如果守护进程在整个文件上得到一把写锁,那么在该守护进程终止时,这把锁将被自动删除,这就简化了复原所需的处理,去除了对以前守护进程实例需要进行清理的有关操作。

实例:保证只运行某个守护进程的一个副本

#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)extern int lockfile(int);int already_running(void){    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);}



四、守护进程的惯例

        1.若守护进程使用锁文件,那么该文件通常存放在/var/run目录中。

        2.若守护进程支持配置选项,那么配置文件通常放在/etc目录中。

        3.守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc* 或 /etc/init.d/*)启动的。

        4.若一守护进程有一配置文件,那么当该守护进程启动时,它读改文件,但在此后一般就不会再查看它.

实例:守护进程重读配置文件

#include "apue.h"#include <pthread.h>#include <syslog.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.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)extern int lockfile(int);static int already_running(void);sigset_t mask;voidreread(void){}void *thr_fn(void *arg){    int err,signo;    err = sigwait(&mask, &signo);    for (;;)    {        if (err != 0)        {            syslog(LOG_ERR, "sigwait failed");            exit(1);        }        switch (signo)        {            case SIGHUP:                syslog(LOG_INFO, "Re-reading configuratttion file");                reread();                break;            case SIGTERM:                syslog(LOG_INFO, "got SIGTERM; exiting");                exit(0);            default:                syslog(LOG_INFO, "unexpected signal %d\n", signo);        }    }    return(0);}intmain(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++;    }    daemonize(cmd);    if (alreadyrunning())    {        syslog(LOG_ERR, "daemon already running");        exit(1);    }    sa.sa_handler = SIG_DFL;    sigemptyset(&sa.sa_mask);    sa.sa_flags = 0;    if (sigaction(SIGHUP, &sa, NULL) < 0)    {        err_quit("%s: can't reatore SIGHUP default");    }    sigfillset(&mask);    if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL)) != 0)    {        err_exit(err, "SIG_BLOCK error");    }    err = pthread_create(&tid, NULL, thr_fn, 0);    if (err != 0)    {        err_exit(err, "can't create thread");    }    exit(0);}int already_running(void){    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);}                                            

实例:守护进程重读配置文件的另一种实现


#include "apue.h"#include <pthread.h>#include <syslog.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h>#include <string.h>#include <errno.h>#include <stdio.h>#include <sys/stat.h>extern int lockfile(int);static int already_running(void);voidreread(void){}voidsigterm(int signo){    syslog(LOG_INFO, "got SIGTERM; exiting");    exit(0);}voidsighup(int signo){    syslog(LOG_INFO, "Re-reading configuration file");    reread();}intmain(int argc, char *argv[]){    char                *cmd;    struct sigaction    sa;    if (cmd = strrchr(argv[0], '/') == NULL)    {        cmd = argv[0];    }    else    {        cmd++;    }    daemonize(cmd);    if (alreadyrunning())    {        syslog(LOG_ERR, "daemon already running");        exit(1);    }    sa.sa_handler = sigterm;    sigemptyset(&sa.sa_mask);    sigaddset(&sa.sa_mask, SIGHUP);    sa.sa_flags = 0;    if (sigaction(SIGTERM, &sa, NULL) < 0)    {        err_quit(LOG_ERR, "can't catch  SIGTERM: %s", strerron(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)    {        err_quit(LOG_ERR, "can't catch  SIGHUP: %s", strerron(errno));        exit(1);    }    exit(0);}int already_running(void){    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);}

0 0