守护进程和inetd超级服务器

13.1 概述

守护进程(daemon)是在后台运行且不与任何控制终端关联的进程。 它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。

守护进程有多种启动的方法:
1、在系统启动阶段,许多守护进程由系统初始化脚本启动。
2、许多网络服务器将由inetd超级服务器启动。inetd一般是由系统初始化脚本启动。
3、cron守护进程按照规则定期执行一些程序,而由他气动执行的程序同样作为守护进程运行。cron也是由方法1启动。
4、at命令用于指定将来某个时刻的程序执行。这些程序的执行时刻的到来,通常由cron守护进程启动执行它们,因此这些程序同样作为守护进程运行。
5、守护进程还可以从用户终端或在前台或在后台启动。
守护进程没有控制终端,所以当有事发生时它们得有输出消息的某种方法可用。syslog函数是输出这些消息的标准方法,它把这些消息发送给syslogd守护进程。

13.2 syslogd守护进程

syslogd守护进程通常由某个系统初始化脚本启动,而且在系统工作期间一直运行。syslogd实现在启动时执行以下步骤:
1、读取配置文件。通常为/etc/syslog.conf的配置文件指定本守护进程可能收取的各种日志消息应该如何处理。
2、创建一个unix域数据包套接字,给他捆绑路径名/var/run/log(某些系统上是/dev/log)。
3、创建一个UDP套接字,给他捆绑端口514(syslog服务使用的端口号)。
4、打开路径名/dev/klog。来自内核中的任何消息都看做是这个设备的输入。

13.3 syslog函数

从守护进程中登记消息的常用技巧是调用syslog函数。
    
    
    
    
  1. #include <syslog.h>
  2. void syslog(int priority, const char *message, ...);
第一个参数priority是级别(level)和设施(facility)两者的结合。
level是日志消息的等级,从0到7。
LOG_EMERG     0     系统不可用(最高优先级)
LOG_ALERT     1     必须立即采取行动
LOG_CRIT      2     临界条件
LOG_ERR       3     出错条件
LOG_WARNING   4     警告条件
LOG_NOTICE    5     正常而重要的条件(默认值)
LOG_INFO      6     通告消息
LOG_DEBUG     7     调试级消息(最低优先级)

facility是用于标识消息发送进程的类型。
LOG_AUTH           安全/授权消息
LOG_AUTHPRIV     安全/授权消息(私用)
LOG_CRON          cron守护进程
LOG_DAEMON        系统守护进程
LOG_FTP           FTP守护进程
LOG_KERN          内核消息
LOG_LOCAL0        本地使用(LOG_LOCAL0 ~ LOG_LOCAL7)
LOG_LOCAL7        本地使用
LOG_SYSLOG        由syslogd内部产生的消息
LOG_USER          用户级消息(默认值)
LOG_UUCP          UUCP系统

    
    
    
    
  1. #include <syslog.h>
  2. void openlog(const char *ident, int options, int facility);
  3. void closelog(void);
openlog可以在首次条用syslog前调用,closelog可以在应用程序不在需要发送日志消息时调用。
ident参数是一个syslog冠于每个日志消息之前的字符串。通常为程序名。
options参数是下表中的一个或多个值得逻辑或构成
LOG_CONS          若无法登记到syslogd守护进程则登记到控制台
LOG_NDELAY       不延迟打开,立即创建套接字
LOG_PERROR       既发送到syslogd守护进程,又登记到标准错误输出
LOG_PID           随每个日志消息登记进程ID

13.4 创建守护进程

1  创建子进程,父进程退出

   这是创建守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成一程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离。在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程是,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。

2  在子进程中创建新会话

   Linux是一个多用户多任务系统,每个进程都有一个进程ID,同时每个进程还都属于某一个进程组,而每个进程组都有一个组长进程,组长进程的标识ID等于进程组的ID,且该进程组ID不会因组长进程的退出而受到影响。会话期是一个或多个进程组的集合,通常,一个会话开始与用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。一个会话期可以有一个单独的控制终端,只有其前台进程才可以拥有控制终端,实现与用户的交互。从shell中启动的每一个进程将继承一个与之相结合的终端,以便进程与用户交互,但是守护进程不需要这些,子进程继承父进程的会话期和进程组ID,子进程会受到发送给该会话期的信号的影响,所以守护进程应该创建一个新的会话期,这个步骤是创建守护进程中最重要的一步,虽然它的实现非常简单,但它的意义却非常重大。在这里使用的是系统函数setsid来实现的。 

   setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个作用:
     让进程摆脱原会话的控制
     让进程摆脱原进程组的控制
     让进程摆脱原控制终端的控制

   由于创建守护进程的第一步调用了fork函数来创建子进程,再将父进程退出。在调用fork函数时,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,还还不是真正意义上的独立开来,而setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。

3  改变当前目录为根目录

   使用fork创建的子进程继承了父进程的当前工作目录。守护进程不应当使用父进程的工作目录,应该设置自己的工作目录,通常可以通过chdir()来完成,一般可以将其设置为根目录,不过有些守护进程需要将它设置到自己特定的工作目录,但此时必须保证所设置的工作目录处于一个不能卸载的文件系统中,因为守护进程通常在系统引导后是一直存在的。

4  重设文件权限掩码

   守护进程从父进程继承来的文件创建方式掩码可能会拒绝设置某些许可权限,文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,可能使守护进程的执行出现问题,因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。

5  关闭文件描述符

  一般情况下,进程启动时都会自动打开终端文件,但是守护进程已经与终端脱离,所以终端描述符应该关闭。用fork函数新建的子进程也会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。

6  忽略SIGCHLD信号

   这一步只对需要创建子进程的守护进程才有必要,很多服务器守护进程设计成通过派生子进程来处理客户端的请求,如果父进程不对SIGCHLD信号进行处理的话,子进程在终止后变成僵尸进程,通过将信号SIGCHLD的处理方式设置为SIG_IGN可以避免这种情况发生。

7  用日志系统记录出错信息

   因为守护进程没有控制终端,当进程出现错误时无法写入到标准输出上,可以通过调用syslog将出错信息写入到指定的文件中。

8  守护进程退出处理

   当用户需要外部停止守护进程运行时,往往会使用 kill命令停止该守护进程。所以,守护进程中需要编码来实现kill发出的signal信号处理,达到进程的正常退出。

你可能感兴趣的:(unix,网络服务器)