守护进程和inetd超级服务器

守护进程是在后台运行且不与任何控制终端关联的进程。既然守护进程没有控制终端,当有事情发生时,它们得有输出消息的某种方法,syslog函数是输出这些消息的标准方法,它把这些消息发送给syslogd守护进程。

源自Berkeley的syslogd实现在启动时执行以下步骤:
1,读取配置文件。通常为/etc/syslog.conf的配置文件指定本守护进程可能收取的各种日志消息应该如何处理。
2,创建一个Unix域数据报套接口,给字捆绑路径名/var/run/log(在某些系统上是/dev/log)
3,创建一个UDP套接口,给它捆绑端口514(syslog服务使用的端口号)
4,打开路径名为/dev/klog。来自内核的任何出错消息看着像是这个设备的输入。
syslog守护进程在一个无限循环中运行:调用select以等待它的在2,3,4步中创建的描述字之一变为可读;读入日志消息,并按照配置文件进行处理。如果守护进程收到SIGHUP信号,那就重新读取配置文件。不过较新的syslogd实现禁止创建UDP套接口,除非管理员明确要求,因为允许任何进程往这个套接口发送UDP数据报造成系统易遭拒绝服务攻击。

syslog函数
既然守护进程没有控制终端,它们就不能把消息fprintf到sterr上,从守护进程中登记消息的常用技巧就是调用syslog函数。
#include <syslog.h>
void syslog( int priority, const char *message, ... );
本函数的priority参数是级别(level)与设施(facility)两者的组合。message参数类似printf格式串,不过增设了%m规范,它将被替换成与当前errno值对应的出错消息。level的默认值是LOG_NOTICE,facility的缺省值为LOG_USER。举例来说,当rename函数调用失败时,守护进程可以执行以下调用:
syslog(LOG_INFO|LOG_LOCAL2,"rename(%s,%s):%m",file1,file2);
level与facility的目的在于,允许在/ect/syslog.conf文件中统一配置来自同一给定设施的所有消息,或者统一配置具有相同级别的所有消息。假如其具有以下两行:
kern.*         /dev/console
local7.debug   /var/log/cisco.log
这两行指定所有内核消息登记到控制台,来自local7设施的所有debug消息添加到文件/va/log/cisco.log的末尾。

daemon_init函数
此函数可以将一个一般的进程变为守护进程,它主完完成以下几步:
1,fork一个子进程,然后终止父进程。
2,调用setsid以创建一个新对话期。
3,将当前工作目录更改为根目录。
4,将文件方式创建屏蔽字设置为0。
5,关闭不再需要的文件描述符。
其代码如下:

#define MAXFD 64

extern int daemon_proc;

int daemon_init( const char *pname, int facility)
{
  int i;
  pid_t pid;

  if ((pid=fork()) < 0)
    {
      return -1;
    }
  else if(pid)
    {
      _exit(0);            /* parent terminate */
    }

  /* child 1 continues... */

  if (setsid() < 0)
    {
      return -1;
    }

  signal( SIGHUP, SIG_IGN );

  if ((pid=fork()) < 0)
    {
      return -1;
    }
  else if(pid)
    {
      _exit(0);
    }

  daemon_proc = 1;
  
  chdir("/");
  
  for (i=0; i<MAXFD; i++)
    {
      close(i);
    }

  open( "/dev/null", O_RDONLY);
  open( "/dev/null", O_RDWR);
  open( "/dev/null", O_RDWR);

  openlog(pname, LOG_PID, facility);

  return 0;
}


initd守护进程
典型的Unix系统中可能有许多服务,它们含有几乎相同的启动代码,每个守护进程在进程表中占据一个表项,然而它们大部分时间处于睡眠状态。
4.3的BSD版本通过一个因特网超级服务器(即inetd守护进程)来简化守护进程的编写。
initd进程使用daemon_init函数的技巧把自己演变成一个守护进程。它接着读入并处理自己的配置文件。通常是/etc/inetd.conf的配置文件指定本超级服务器处理哪些服务以及当一个服务请求到达时该怎么做。该文件中每行包含的字段如下:
service-name        必须在/etc/services文件中定义的服务名
socket-type         stream或dgram
protocol            必须在/etc/protocols文件定义中定义:tcp或udp
wait-flag           对于TCP一般为nowait,对UDP一般为wait
login-name          来自/etc/password的用户名,一般为root
server-program      调用exec指定完整的路径名
server-program-arguments  调用exec指定的命令行参数
比如:
ftp   stream  tcp nowait root /usr/bin/ftpd   ftpd  -l

initd守护进程的工作流程
1,在启动阶段,读入/etc/inetd.conf文件并给该文件中指定的每个服务创建一个适当类型的套接口。
2,为每个套接口调用bind,指定捆绑相应服务器的众所周知端口和通配IP地址。
3,对于每个TCP套接口,调用listen以接受外来的连接请求。对于数据报套接口则不执行本步骤。
4,创建完毕所有套接口后,调用select等待其中任何一个套接口变为可读。
5,当select返回指出某个套接口已可读之后,如果该套接口是一个TCP套接口,而且其服务器的wait-flag值为nowait,那就调用accept接受这个新连接。
6,inetd守护进程调用fork派生进程,并由子进程处理服务请求。
7,如果第5步中select返回的是一个字节流套接口,那么父进程必须关闭接受了的已连接套接口,父进程再次调用select,等待下一个变为可读的套接口。

 

参考《UNIX网络编程》(卷1)第十三章:守护进程和inetd超级服务器 。

另附上 守护进程(xinetd)的参考链接: http://zhumeng8337797.blog.163.com/blog/static/10076891420108205392785/

posted @ 2014-03-19 17:05  不止所见  阅读(1046)  评论(0编辑  收藏  举报