守护进程(daemon)

来源:互联网 发布:mac 设置homekit 编辑:程序博客网 时间:2024/06/03 17:06
守护进程常常在系统引导装入时启动,在系统关闭时终止。一般说Daemon程序在后台运行,
是因为它没有控制终端,无法和前台的用户交互。Daemon程序一般都作为服务程序使用,
等待客户端程序与它通信。我们也把运行的Daemon程序称作守护进程。

一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,
所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,
无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。

 一般(1) 在系统启动的时候由启动脚本启动,这些启动脚本通常放在/etc/rc.d目录下
/etc/init.d/xinetd
XINETD_BIN=/usr/sbin/xinetd
case "$1" in
    start)
startproc -p $XINETD_PIDFILE -t 1 $XINETD_BIN -pidfile $XINETD_PIDFILE
(2) 由cron定时启动
/etc/cron.d/EISlogging
4-59/10  * * * * root /var/lib/EISanalytics/cron/logNet.pl
(3) 由nohup命令启动
Nohup命令忽略所有挂断(SIGHUP)信号
终端退出会发SIGHUP信号,可能导致终端上运行程序的异常退出
语法:nohup Command [ Arg … ] [ & ]
e.g: nohup xxx.sh &
情况下守护进程可以通过以下几种方式产生:


standalone daemon: 可以自行启动而不必透过其他机制的管理(not depend on another service to be activated); 

standalone daemon 启动并加载到内存后就一直占用内存与系统资源。最大的优点就是:响应速度较快。
常见的 standalone daemon 有 WWW 的 daemon(httpd)

super daemon是xinetd,有一类服务由xinetd来负责唤起。当没有客户端的要求时,
各项服务都是未启动的状态,等到有来自客户端的要求时,super daemon才唤醒相对应的服务。
当客户端的要求结束后,被唤醒的这个服务就会关闭并释放系统资源。
这种机制的好处:
(1)由于 super daemon 负责唤醒各项服务,因此 super daemon 可以具有安全控管的机制
(2)由于服务在客户端的联机结束后就关闭,因此不会一直占用系统资源。
这种机制的缺点:
因为有客户端的联机才会唤醒该服务,而该服务加载到内存需要一定的时间,因此服务的反应时间会比较慢一些,

常见的super daemon 所管理的服务有tftp

1. change the value of key "disable" to "no"
vi /etc/xinetd.d/tftp
service tftp
{
        socket_type             = dgram
        protocol                = udp
        wait                    = yes
        flags                   = IPv6 IPv4
        user                    = root
        server                  = /usr/sbin/in.tftpd
        server_args             = -s /tftpboot
        disable                 = yes  
}
2. restart xinetd service
service xinetd restart


编写daemon的步骤:
(1)蔽一些有关控制终端操作的信号
为防止在守护进程还没有运行之前,控制终端收到干扰exit或者挂起
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
(2)在后台运行。
为避免挂起控制终端而将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
      if(pid=fork())
          exit(0);//是父进程,结束父进程,子进程继续
(3)脱离控制终端,登录会话和进程组
 进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。
这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。控制终端,登录会话和进程组通常是从父进程继承下来的。
我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
    setsid()
(4)关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符。
如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
  #define   NOFILE   256  ;//不同系统中不同数值
  for(i=0;i<NOFILE;i++)
         colse(i);
(5)改变当前工作目录
进程活动时,其工作目录所在的文件系统不能卸下。
一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/")
   chdir("/");
(6)重设文件创建掩模
    进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除0:
   umask(0);

umask计算方法:  777 - 文件当前权限
(7)处理SIGCHLD(子进程退出信号)信号
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。
如果父进程不等待子进程结束,子进程 将成为僵尸进程(zombie)从而占用系统资源。
如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地 将SIGCHLD信号的操作设为SIG_IGN。

 signal(SIGCHLD,SIG_IGN);

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>

void run_daemon(char *idents, int facility)
{
        // 1. ignore some signals which may control or affect the terminal
        signal(SIGTTOU, SIG_IGN);
        signal(SIGTTIN, SIG_IGN);
        signal(SIGTSTP, SIG_IGN);
        signal(SIGHUP, SIG_IGN);

        // 2. let child process run in background
        pid_t pid;
        if ((pid = fork()) > 0)
                exit(0);
        else if (pid < 0)
        {
                perror("fork process error");
                exit(-1);
        }

        //3. detach from terminal, session, process group
        setsid();

        //4. close file descriptor
        for (int i = 0; i < NOFILE; ++i)
                close(i);
        open("/dev/null", O_RDWR);

        //5. change to root dir
        chdir("/");

        //6. reset file mask
        umask(0);

        //7. handle SIGCHLD
        signal(SIGCHLD, SIG_IGN);

        openlog(idents, LOG_PID, facility);
}
int main(int argc, char *argv[])
{
        time_t t;
        run_daemon(argv[0], LOG_KERN);

        for (;;)
        {
                sleep(5);
                t = time(NULL);
                syslog(LOG_INFO, "current time: %s", asctime(localtime(&t)));
        }

        return 0;
}

tail -n 2 /var/log/messages
Apr  9 07:31:12 esekilx6451 ./daemontest[19915]: current time: Thu Apr  9 07:31:12 2015
Apr  9 07:31:17 esekilx6451 ./daemontest[19915]: current time: Thu Apr  9 07:31:17 2015



0 0