Linux 守护进程 、inetd超级服务器

守护进程

通常源于系统初始化脚本启动。它们于控制台脱离关联。

启动方式:

  • 系统启动阶段由系统初始化脚本启动,通常位于 /etc /etc/rc 目录,启动一开始有root权限
  • 由inetd启动
  • cron 按规则定期启动
  • at 命令指定于某个时刻执行,通常由cron启动它们
  • shell 通过 nohup xxx > /dev/null 2>&1 & 启动
  • 通过代码,fork出子进程再杀死父进程,得到的孤儿进程也是守护进程
    创建守护进程
    void daemon_init(){
      // 创建孤儿进程
      pid_t pid = fork();
      if (pid < 0) {
        exit(-1);
      } else if (pid > 0) {
        exit(0);
      }
      // 创建新的会话
      if (setsid() < 0) {
        exit(-1);
      }
      // 处理 SIGHUP 信号
      //    此信号在终端断开时发出,默认会退出进程
      signal(SIGHUP, SIG_IGN);
      // 再次 fork 保证进程不是会话首进程,不再拥有控制终端
      if (pid < 0) {
        exit(-1);
      } else if (pid > 0) {
        exit(0);
      }
      // 更改工作目录
      chdir("/");
      // 设置权限掩码
      if (umask(0) < 0) {
        perror("umask");
        exit(-1);
      }
      // 关闭文件描述符,并重定向 stdio stdout stderr
      for (int i = 0; i < MAXFD /*自定义宏*/; ++i) {
        close(i);
      }
      open("/dev/null", O_RDONLY);
      open("/dev/null", O_RDWR);
      open("/dev/null", O_RDWR);
    
      // 守护进程开始执行任务
      while (1) {
        // ...
      }
      return 0;
    }

syslogd 守护进程

syslogd 启动时:

  1. 读取 /etc/syslog.conf 配置,指定了可能收取的各种log
  2. 创建一个 unix 域套接字,绑定到 /dev/run/log
  3. 创建一个UDP套接字,绑定到端口514(新版本禁止了这个端口防止 Dos)
  4. 打开 /dev/klog 这个设备接收内核输出的错误消息
  5. 调用select等待前3个描述符,收到 SIGHUP 信号则重新读配置文件
#include <sys/syslog.h>

void openlog (const char *__ident, int __option, int __facility);
void syslog (int __pri, const char *__fmt, ...);
/*
第一个参数用于指定level和facility,如:
LOG_EMERG LOG_ERR LOG_WARNING LOG_INFO
LOG_AUTH LOG_CRON LOG_DAEMON
*/
void closelog (void);

inetd/xinetd 守护进程

inetd 处理普通守护进程的大部分启动细节,单个inetd进程能为多个服务等待外来客户请求,减少了系统进程总数。其不适合服务密集型服务器

配置文件示例
 # /etc/xinetd.conf

# 禁用所有网络服务,除了我们明确启用的服务
defaults
{
    instances               = 60        # 允许的最大并发连接数
    log_type                = SYSLOG    # 使用 syslog 记录日志
    log_on_success          = HOST PID   # 记录连接成功的信息
    log_on_failure          = HOST       # 记录连接失败的信息
    cps                     = 25 30     # 控制连接速率:25个连接/30秒
    per_source              = 10        # 每个来源IP的最大连接数
    v6only                  = no        # 启用 IPv4 和 IPv6 支持
}

# 配置一个 echo 服务,允许客户端发送文本,返回相同的文本
service echo
{
    disable                 = no        # 禁用该服务:no 表示启用
    socket_type             = stream    # 使用流套接字
    protocol                = tcp       # 使用 TCP 协议
    wait                    = no        # 不等待服务程序退出,允许并发连接
    user                    = nobody    # 服务运行的用户
    server                  = /usr/bin/echo   # 服务程序的路径
    log_on_success          = HOST DURATION   # 记录连接成功和连接持续时间
    log_on_failure          = HOST       # 记录连接失败
}

# 配置一个 daytime 服务,返回服务器当前时间
service daytime
{
    disable                 = no
    socket_type             = stream
    protocol                = tcp
    wait                    = no
    user                    = nobody
    server                  = /usr/sbin/daytime   # 服务程序的路径
    log_on_success          = HOST DURATION
    log_on_failure          = HOST
}

如果inted指定 nowait,每个请求派生一个子进程处理:

  1. 读入配置文件,为每个服务创建一个套接字并加入 select
  2. 每个套接字调用 bind listen
  3. 创建完毕后调用select阻塞
  4. 调用fork派生进程,子进程处理服务请求
  5. 子进程将套接字描述符 dup2 到标准io,关闭其他所有描述符
  6. 根据配置文件中用户名等调整自身,使用 exec 执行

指定 wait 要求inetd必须在这个套接字再次称为select调用的候选套接字前等待当前服务该套接字的子进程终止:

  1. fork 返回到父进程时,父进程报错子进程id,通过 waitpid确定终止时间
  2. 通过 FD_CLR 关闭这个套接字在 select 中对应位,直到子进程终止对该套接字的接管
  3. 子进程终止时父进程收到 SIGCHLD 信号,父进程打开select所用描述符集中对应的位
posted @ 2023-08-05 17:08  某某人8265  阅读(66)  评论(0编辑  收藏  举报