zoukankan      html  css  js  c++  java
  • UNP学习第13章 守护进程和inetd超级服务器

    Unix系统中的syslogd守护进程通常由某个系统初始化脚本启动,而且在系统工作期间一直运行。

    源自Berkeley的syslogd实现在启动时执行以下步骤。

    (1)读取配置文件。通常为/etc/syslog.conf的配置文件指定本守护进程可能收取的各种日志消息,应该如何处理。

    (2)创建一个Unix域数据报套接字,给它捆绑路径名/var/run/log

    (3)创建一个UDP套接字,给它捆绑端口514。

    (4)打开路径名/dev/klog。

    一、syslog函数

    #include <syslog.h>
    
    void syslog(int prioriry, const char *message, ...);
    priority:级别level和设施facility两者的组合
    message参数类似printf的格式串,不过增加了%m,它将被替换成与当前errno值对应的出错消息。

    日志消息的level可以是0~7,他们按从高到低的顺序排列的。如果发送者未指定level值,那就默认为LOG_NOTICE。

    日志消息包含一个用于标识消息发送进程类型的facility。默认LOG_USER

    举例来说,当rename意外失败时,守护进程可以执行以下调用:

    syslog(LOG_INFO|LOG_LOCAL2, "rename(%s, %s):%m", file1, file2);

    #include <syslog.h>
    
    void openlog(cosnt char *ident, int options, int facility);
    void closelog(void);
    ident:有syslog关于每个日志消息之前的字符串。通常的值是程序名
    options:一个或多个常值的逻辑或构成
    facility:

     options的参数

    二、守护进程

     下面有一个daemon_init函数,调用它可使一个进程变成守护进程。

     1 #include "unp.h"
     2 #include <syslog.h>
     3 
     4 #define MAXFD 64
     5 
     6 extern int daemon_proc;     /* defined in error.c */
     7 
     8 int
     9 daemon_init(const char *pname, int facility)
    10 {
    11     int i;
    12     pid_t pid;
    13 
    14     if((pid = Fork()) < 0)
    15         return(-1);
    16     else if(pid)
    17         _exit(0);           /* parent terminates */
    18     /* child 1 continues ... */
    19     if(setsid() < 0)        /* become session leader */
    20         return(-1);
    21     Signal(SIGHUP, SIG_IGN);
    22     if((pid = Fork()) < 0)
    23         return(-1);
    24     else if(pid)
    25         _exit(0);           /* child 1 terminates */
    26     /* child 2 continues ... */
    27     daemon_proc = 1;        /* for err_XXX() functions */
    28     chdir("/");             /* change working directory */
    29     /* close off file descriptors */
    30     for(i=0;i<MAXFD;i++)
    31         close(i);
    32     /* redirect stdin, stdout, and stderr to /dev/null */
    33     open("/dev/null", O_RDONLY);
    34     open("/dev/null", O_RDWR);
    35     open("/dev/null", O_RDWR);
    36     openlog(pname, LOG_PID, facility);
    37     return(0);
    38 }
    • 首先调用fork,然后终止父进程,留下子进程继续运行。如果进程是以shell命令方式从前台启动,当父进程终止时,shell就认为命令已完成。这可以使子进程运行在后台。子进程继承了父进程的进程组号,但它拥有自己的进程号。这就保证了这个子进程不是进程组头。
    • setsid创建一个新的登录会话。这个进程变成新会话的会话头和新进程组的组长,不再有控制终端。
    • 忽略SIGHUP信号并再次调用fork,再次调用fork的目的是确保本守护进程将来即使打开了一个终端设备,也不会自动获得控制终端。
    •  为错误处理函数设置标识
    • 改变工作目录
    • 关闭所有打开的文件描述符
    • 将stdin、stdout、stderr重定向到/dev/null
    • 使用syslog处理错误

    三、inetd守护进程

    inetd守护进程的工作流程

    1. 启动时读/etc/inetd.conf文件并给文件中指定的所有服务创建一个响应类型的套接口。inetd能处理的服务器的数目依赖于它最多能创建的描述字的数目。每个新创建的套接口都被加入到select调用所用到的描述字集中。
    2. 为每个套接口调用bind,给它们捆绑服务器的众所周知端口和通配地址。它们的TCP或UDP端口号时通过调用getservbyname获得的,其中使用了配置文件中的service-name和protocol作为参数。
    3. 对TCP套接口调用listen,以接受外来的连接请求。对数据报套接口则不做这一步。
    4. 所有套接口建立后,调用select等待这些套接口变为可读。当在TCP套接口上到来一个新的连接请求或UDP套接口上到来一个数据报时他们会变成可读。inetd在大部分时间里阻塞在select调用上,等待有一个套接口变成可读。
    5. select返回一个可读的套接口后,如果是一个TCP套接口,就调用accept接受这个新的连接。
    6. inetd守护进程fork,由子进程处理服务请求。

    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    如何获取Apollo上项目下的所有namespace?
    从源码研究如何不重启Springboot项目实现redis配置动态切换
    用 Explain 命令分析 MySQL 的 SQL 执行
    MySQL死锁系列-常见加锁场景分析
    带你100% 地了解 Redis 6.0 的客户端缓存
    Java 数据持久化系列之 HikariCP (一)
    MySQL的死锁系列- 锁的类型以及加锁原理
    Java 数据持久化系列之池化技术
    Redis Cluster 的数据分片机制
    Redis 命令执行过程(下)
  • 原文地址:https://www.cnblogs.com/ch122633/p/8495227.html
Copyright © 2011-2022 走看看