荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Peter (小飞侠), 信区: Program
标  题: Daemon编程与启动
发信站: BBS 荔园晨风站 (Thu Jan 21 14:55:54 1999), 站内信件 (WWW POST)

背景知识
    当我们登录进入UNIX操作系统时,系统为我们产生一个登录shell
, 登录shell的标准输入、输出和错误输出指向一个终端文件。在登
录shell中,假如我们键入如下命令:
    $proc1 | proc2 &$proc3 | proc4 | proc5这两个命令产生的进
程组和登录shell这一进程组的集合称作一个会话(session),登录she
ll被称作会话的会话头(session leader),与登录shell相连的终端文
件被称作控制终端(control terminal)。一个会话最多有一个控制终
端,也可能没有控制终端。有控制终端的会话的会话头被称作控制进
程(control proces) 。一个会话中的进程组可分为前台进程组和后
台进程组,当会话有控制终端时,则它有一个前台进程组,会话中的其
他进程组都是后台进程组。只有前台进程组可以接收终端输入,当我
们键入终端的控制键时,如中断键(一般为control-c)或者退出键(一
般为control-\),这会引起中断信号SIGINT或者退出信号SIGQUIT发送
给前台进程组中的所有进程。当终端挂起时,IGH UP信号发送到会话
中的每一个进程,不管是前台进程组,还是后台进程组(这一关系如下
图所示)。
图1
    一个进程可以调用setsid函数建立一个新的会话,如果调用进程
不是进程组头,这个函数产生一个新的会话,同时发生下面三件事:
    ·这个进程变为新会话的会话头,并且是会话中的唯一进程。
    ·这个进程变为新进程组的进程组头,新进程ID是调用进程的ID

    ·进程没有控制终端,如果调用setsid之前进程拥有控制终端,调
用后不再拥有。
    如果调用进程是进程组头,则该函数调用失败。
    什么是Daemon
    daemon的意思是精灵或恶魔,在UNIX中一般翻译为守护或驻留进
程。除非意外情况或人为干预,daemon一般在操作系统启动时运行,关
机时结束。daemon通常作为客户   /服务器模式中的服务器进程。UN
IX操作系统中有大量的daemon日复一日地为客户服务。其主要特点是
:daemon是一种后台进程,没有控制终端,因此不受终端控制键的影响,
也不会由于中断挂起而退出。而且它是进程组头和会话头,是进程组
和会话中唯一的进程。
    daemon编码基本原则如下:
    (1)调用fork并且结束父进程。这样做可以达到两个目的。首先,
如果daemon是在shel命令中启动的,结束父进程会使shell认为命令已
经结束。第二,子进程继承了父进程的进程组ID同时得到一个新的进
程ID,所以我们可以保证子进程不是进程组头,这是调用setsid函数的
先决条件。
    (2)调用setsid产生一个新的会话。进程变为新会话的会话头和
新进程组的进程组头,同时进程没有控制终端。以上这些满足了daemo
n的特点。也就是说,进程已经基本成为daemo进程。但是还有其他一
些工作要做。
    (3)关闭所有打开的文件描述字。
    (4)改变当前的工作目录。
    (5)重设文件存取建立的屏蔽码。
    (6)忽略SIGCLD信号。作为server,当daemon接收到client的请求
时,一般fork一个子进程为client服务。当子进程退出时,如果daemon
未调用wait等待子进程结束,则子进程成为o mbie进程;对每一个zomb
ie进程,内核会保留一些有关的信息,如进程ID,以便父进程调用wit
。如果有大量client请求服务,必将产生很多zombie进程占用系统内
存,忽略SIGCLD信号则可避免zombie进程的积累。
    下面是使用前面的编码原则编写的简单的daemon进程。
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    int daemon_init(void){
    pid_t    pid;
      if ( (pid = fork()) < 0)
      return(-1);
      else if (pid != 0)
      exit(0);    /* 父进程退出 */
      /* 子进程继续 */
      setsid();         /* 变为会话头 */
      chdir("/");       /* 改变工作目录 */
      umask(0);         /* 重设文件存取建立的屏蔽码 */
      signal(SIGCLD,SIG_IGN); /* 忽略SIGCLD信号 */
      return(0);
    }

    Daemon日志
    守护进程没有控制终端,所以当daemon希望处理错误信息时,我们
不能使用printf函数向终端写错误信息。当然,daemon可以打开一个
文件,在此文件中记录错误信息。这样的文件应该以添加方式打开,以
便前边的信息不被后边的信息所覆盖。但使用这种方法存在一些问题
:随着日志信息的增多,系统管理员必须不断地对该文件进行检查,使
之保持在一定长度范围内,这对系统管理员是一个比较麻烦的事情。
此外,如果我们想使用其他文件记录日志时,必须重新修改daemon源文
件。因此,在UNIX系统中提供了syslogd daemon专门用于记录日志信
息,使用syslogd daemon可以使我们灵活安排日志使用的文件。
    syslogd   daemon在启动时读配置文件/etc/syslog.conf决定不
同的信息分别存放在哪个文件中,例如,可以指定紧急信息直接送往控
制台,而一般的告警事件记录在某一文件中。
    使用syslogd的编程接口是:
      #include<syslog.h>
      void openlog(char *ident,int option,int facility);
      void syslog(int priority,char *format,……);
      void closelog(void);
      在这些系统调用中,我们可以指定信息的来源、信息的重要程
度,可以在信息上附加其他信息。这些接口的具体使用方法和配置文
件syslog.conf的格式,可参考系统提供的手册。
    Daemon的重配置
    daemon在启动时首先读配置文件对daemon中使用的参数进行配置
。当我们想改变某些参数时,我们可以编辑配置文件。当然,编辑好配
置文件后并不能立刻改变了aemon中的参数,d aemon必须重新读配置
文件。一种方法是重新启动系统,当然这种方法是一种最笨的方法。
好一点的方法是使用ps命令找出daemon进程ID,结束该进程然后重新
运行demon。这种办法的缺点是:如果某一进程正在与daemon通信时,
会使该进程感觉到。最好的办法是:daemon收到某个事先约定的信号
时,重新读配置文件,这样就不必中断daemon正常运行。
    这样的daemon程序如下所示:
    void read_config_file()
    {
       int fd;
       fd=open("config_file",O_RDONLY);
       /*读配置文件并配置参数*/
       close(fd);
       signal(SIGHUP,read_config_file);
    }
    main()
    {
       read_config_file();
       /*进入服务循环*/
       while(1)
       {
       }
    }
    需要注意的是:在read_config_file中,最好不要使用fopen,frea
d等函数,因为它们是不可重入函数(nonreentarnt function),否则可
能产生无法预测的结果。
    假如我们想重新配置syslogd daemon参数,编辑好syslog.conf文
件后,可利用如下的命令,对syslogd进行配置:
    $ps -ae | grep syslogd  /* 查出syslogd daemon的ID */$kil
l -HUP processID* 强迫daemon重新读配置文件并配置 */

    Daemon的运行
    一般情况下,daemon在系统启动时自动启动,要使daemon随系统的
启动而启动,必须在相应的启动运行脚本中加入一些内容。当UNX系统
启动时,内核产生一个init进程,它的进程ID 为1,由init进程执行dae
mon启动脚本。
    在BSD中,init进程执行/etc/rc.boot、/etc/rc和/etc/rc.local
脚本。rc.local脚本是存放运行本地daemon命令的地方。这些命令是
简单的B shell命令。因此启动daemon只需在rc.local中加入运行该d
aemon的命令即可。
    在SVR4中情况比较复杂。init进程支持多个运行级别,从0到6和s
(单用户)。所谓运行级别可以看作是系统的软件配置。每种运行级别
选择运行不同的进程。运行级别不同时,ini所采取的动作也不同,这
由配置文件和多个shell脚本文件控制。其中配置文件是/etc/initab
文件。下面是该文件的一个例子:
    init:2:initdefault:
    stty::sysinit:stty 9600 clocal icanon echo opost onlcr i
enqak ixon icrnl in par </dev/systty
    link::wait:/bin/sh -c "rm -f /dev/syscon; ln /dev/systty
/dev/syscon" >/dev /console 2>&1
    rc  ::wait:/etc/rc </dev/console >/dev/console 2>&1 # sy
stem initializatio
    halt:6:wait:/usr/lib/X11/ignition/shutdown.ksh\
    # NOTE: runlevel 6 is reserved for system shutdown.
    vue :234:respawn:/etc/vuerc  #VUE validation and invocat
ion
    inittab文件的格式是:
    标记::运行级别:动作:进程每行的结尾由回车键结束。#后是对
该行的注释。每行中的各段由冒号隔开,各段的意义如下:
    ·标记:用于标记这一行。
    ·运行级别:每行所处的运行级别。例如,如果init的运行级别是
1,则它将处理标有1的一行内容。当该段空时,则任何级别都需运行该
行命令。
    ·动作:告诉init管理该daemon时应该采取的策略。最普通的策
略是wait,即该daemon只在系统启动时被运行,不管以后是否中断。re
spawn的意思是:系统监视daemon的运行状况,当它中断时自动重新启
动。还有其他的策略值这里就不一一介绍了。
    ·进程:被执行的B shell脚本或shell命令。
    我们可以直接在inittab文件中写入daemon启动命令,或者写在B
shell脚本中

--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 210.39.0.80]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店