第十三章:守护进程

13.1:引言

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

13.2:守护进程的特征

查看守护进程: ps -axj

注意:

大多数守护进程都以超级用户特权运行。没有一个守护进程拥有控制终端,其终端名设置为问号(?),终端前台进程组ID设置为-1。内核守护进程以无控制终端方式启动。用户层守护进程缺少控制终端可能是守护进程调用了setsid的结果。所有用户层守护进程都是进程组的组长进程一会会话的首进程,而且是这些进程组和会话中的唯一进程。最后,应当引起注意的是大多数守护进程的父进程是init进程。

13.3:编程规则

在编写守护进程时需要遵循一些基本规则,以便防止产生并不需要的交互作用。下面先说明这些规则,然后给出按照这些规则编写的函数daemonize。

(1)首先要做的是调用umask将文件模式创建屏蔽字设置为0。

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

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

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

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

(6)某些守护进程打开/dev/null使其具有文件描述符0、1、2,这样,任何一个试图读标准输入、写标准输出或标准出错的库例程都不会产生任何效果。

实例:

#include "apue.h"
#include <syslog.h>
#icnldue <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;

    // clear file creation mask
    umask(0);

    // get maximum number of file descriptors
    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
    {
        err_quit("%s:can‘t get file limit", cmd);
    }

    // become a session leader to lose controlling tty
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can‘t fork", cmd);
    }
    else if (pid != 0)
    {
        exit(0);
    }
    setsid();

    // ensure future opens won‘t allocate controlling ttya
    sa.sa_handle = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flag = 0;
    if (sigaction(SIGHUB, &sa, NULL) < 0)
    {
        err_quit("%s: can‘t ignore SIGHUB");
    }
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can‘t fork", cmd);
    }
    else if (pid != 0)
    {
        exit(0);
    }

    // change the current working directory to the root so we won‘t prevent file systems from being unmounted
    if (chdir("/") < 0)
    {
        err_quit("%s: can‘t change directory to /");
    }

    // close all open file descriptors
    if (rl.rlim_max == RLIM_INFINITY)
    {
        rl.rlim_max = 1024;
    }
    for (i = 0; i < rl.rlim_max; i++)
    {
        close(i);
    }

    // attach file descriptors 0, 1, and 2 to /dev/null
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    // initialize the log file
    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);
    }
}

13.4:出错记录

守护进程由于没有控制终端,所以输出错误信息需要特殊处理,可以使用syslog日志记录机制。

13.5:单实例守护进程

为了正常运作,某些守护进程实现为单实例的,也就是在任一时刻只运行该守护进程的一个副本。文件锁和记录锁机制是一种方法的基础,该方法用来保证一个守护进程只有一个副本在运行。如果每一个守护进程创建一个文件,并且在整个文件上加上一把写锁,那就只允许创建一把这样的写锁,所以在此之后如试图再创建一把这样的写锁就将失败,以此向后续守护进程副本指明已有一个副本正在运行。

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

实例:函数说明了如果使用文件和记录锁以保证只运行某守护进程的一个副本。

#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_IPOTH)

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

13.6:守护进程的惯例

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

  • 若守护进程使用锁文件,那么该文件通常存放在/var/run目录中。
  • 若守护进程支持配置选项,那么配置文件通常存放在/etc目录中。配置文件的名字通常是name.conf,其中name是该守护进程或服务的名字。例如,syslogd守护进程的配置文件在/etc/syslog.conf。
  • 守护进程可用命令行启动,但通常它们是由系统初始化脚本之一(/etc/rc*或/etc/init.d/*)启动的。如果守护进程终止时,应当自动地重新启动它,则我们可在/etc/inittab中为该守护进程包括_respawn记录项,这样,init就将重新启动该进程。
  • 若一守护进程有以配置文件,那么该守护进程启动时,它读该文件,但在此之后一般就不会再查看它。

实例:所示程序说明了守护进程可以重读其配置文件的一种方法。该程序使用sigwait以及多线程。

时间: 2024-10-12 21:51:13

第十三章:守护进程的相关文章

《APUE》读书笔记第十三章-守护进程

守护进程 守护进程是生存期较长的一种进程,它们常常在系统自举时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的.UNIX系统由很多守护进程,它们执行日常事务活动. 本章主要介绍守护进程的结构,以及如何编写守护进程程序和守护进程如何报告错误情况. 一.守护进程的编程规则 (1)首先要做的是调用umask将文件模式创建屏蔽字设置为0.这是由于继承得来的文件模式创建屏蔽字可能会拒绝设置某些权限. (2)调用fork,然后使父进程退出(exit). (3)调用setsid以创建

apue学习笔记(第十三章 守护进程)

本章将说明守护进程结构,以及如何编写守护进程程序. 守护进程,也就是通常说的Daemon进程,是Unix中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件. 编程规则 在编写守护进程程序时需遵循一些基本规则,以防止产生不必要的交互作用.下面将说明这些规则. 1.调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0) 2.调用fork,然后使父进程exit,保证了子进程不是进程组的组长进程,是下面进行setsid调用的先决条件 3

第十三章 守护进程和inetd超级服务器

书看到这里,我发现我应该把前面steven写的那些包裹函数之类的,实现出自己的一个库来, 方便每次调用,不用自己一遍一遍的写了.库就用libsock.so了. 先创建自己的头文件:unpv13.h #ifndef UNPV13_H_ #define UNPV13_H_ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <stri

《Unix环境高级编程》读书笔记 第13章-守护进程

1. 引言 守护进程是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.它们没有控制终端,在后台运行. 本章说明守护进程结构.如何编写守护进程程序.守护进程如何报告出错情况. 2. 守护进程的特征 基于BSD的系统下执行:ps -axj -a 显示由其他用户所拥有的进程的状态:-x 显示没有控制终端的进程状态:-j 显示与作业有关的信息 基于System V的系统下执行:ps -efj Linux下执行以上两个命令输出一致 常见的守护进程: kswapd,内存换页守护进程.

Unix网络编程代码 第13章 守护进程和inetd超级服务器

1. 概述 守护进程是在后台运行且不与任何控制终端关联的进程.unix系统通常有很多守护进程在后台运行,执行不同的管理任务.    守护进程没有控制终端通常源于它们由系统初始化脚本启动.然而守护进程也可能从某个终端由用户在shell提示符下键入命令行启动,这样的守护进程必须亲自脱离与控制终端的关联,从而避免与作业控制,终端会话管理,终端产生信号等发生任何不期望的交互,也可以避免在后台运行的守护进程非预期的输出到终端.    守护进程有多种启动方法:    1.在系统启动阶段,许多守护进程由系统初

第13章守护进程总结

1 编写守护进程基本规则 1)umake(0)将文件模式创建屏蔽字设置为0 2)fork之后,父进程exit 3)子进程调用setsid 4)更改工作目录chdir("/") 5)关闭所有打开的文件描述符 6)在/dev/null上打开文件描述符1,2,3 2 守护进程出错处理 产生日志消息的三种方式: 1)内核进程调用log函数写日志消息到/dev/klog 2)用户进程调用syslog函数写日志消息到/dev/log 3)网络主机上的进程发送日志消息UDP包到514端口 syslo

UNP学习第13章 守护进程和inetd超级服务器

Unix系统中的syslogd守护进程通常由某个系统初始化脚本启动,而且在系统工作期间一直运行. 源自Berkeley的syslogd实现在启动时执行以下步骤. (1)读取配置文件.通常为/etc/syslog.conf的配置文件指定本守护进程可能收取的各种日志消息,应该如何处理. (2)创建一个Unix域数据报套接字,给它捆绑路径名/var/run/log (3)创建一个UDP套接字,给它捆绑端口514. (4)打开路径名/dev/klog. 一.syslog函数 #include <sysl

第十三章 进程、线程类的实现

                                        第十三章   进程.线程类的实现         多线程是指在一个进程内可以同时运行多个任务,每个任务由一个单独的线程来完成.线程是进程运行的基本单位,一个进程中可以同时运行多个线程.如果程序被设置为多线程方式,可以提高程序运行的效率和处理速度. 多个线程共用一个进程的资源:进程的调度.切换是在10ms的"时钟滴答"定时中断程序里进行.如果一个线程获得CPU,那么在下一个Tick到来前:是不可能被切换出去的

Gradle 1.12翻译——第十九章. Gradle 守护进程

有关其他已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或访问:http://gradledoc.qiniudn.com/1.12/userguide/userguide.html 本文原创,转载请注明出处:http://blog.csdn.net/maosidiaoxian/article/details/41343615 关于我对Gradle的翻译,以Github上的项目及http://gradledoc.qin