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

第十三章    守护进程



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

        系统进程依赖于操作系统实现。父进程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>


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)
    {
        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;

void
reread(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);
}

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++;
    }

    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);


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++;
    }

    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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值