| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
系列文章导航:《Unix 网络编程》笔记
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
守护进程的特点:
守护进程的启动
由系统初始化脚本启动,通常位于 /etc/rc 或 /etc 目录下,由这些脚本启动的守护进程一开始就有用超级用户特权
如:inetd 超级服务器、Web 服务器、sendmail 服务器
由 inetd 超级服务器启动,它监听网络请求
cron 守护进程按照规则定期执行一些程序,由它执行的程序同样作为守护进程运行
at 命令用于在未来某个时刻执行一些程序
守护进程还可以从用户终端或在前台或者后台启动,这么做往往是为了测试
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
由于这段代码涉及的知识点比较多,所以首先需要补充一点操作系统的知识:Linux 进程、进程组、会话周期、控制终端
按照上文中的描述阅读下述代码:
- 在父进程(此时是一个进程组的组长)中使用fork()产生子进程(将来的守护进程由它产生)
- 调用setsid(),用于生成一个新的会话 注意如果当前进程是会话组长时,调用失败。第一点已经可以保证进程不是会话组长了,所以setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话对控制终端的独占性,进程同时与控制终端脱离
- 禁止进程重新打开控制终端 第二步之后,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端,在上面的控制终端中已经提到了只有会话组长才能打开控制终端;
#include <syslog.h>#include "unp.h"#define MAXFD 64extern int daemon_proc; /* defined in error.c */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 terminates */ /* child 1 continues... */ // setsid 使得当前进程变为: // - 新会话的会话头进程 // - 新进程组的进程组头进程 if (setsid() < 0) return (-1); // 忽略 SIGHUP 信号 // 会话头进程终止后,所有其他会话进程都会收到一个该信号 Signal(SIGHUP, SIG_IGN); if ((pid = Fork()) < 0) return (-1); else if (pid) _exit(0); /* child 1 terminates */ /* child 2 continues... */ // 告知错误处理函数使用 syslog 而不是 fprintf daemon_proc = 1; /* for err_XXX() functions */ // 改变工作目录,否则就是在当前目录下运行,可能产生破坏 chdir("/"); // 关闭前 64 个描述符,即使有些本身就没有打开 for (i = 0; i < MAXFD; i++) close(i); /* redirect stdin, stdout, and stderr to /dev/null */ // 这样设置会占用标准输入、标准输出和标准错误输出的描述符 // 防止在服务器环境下,套接字占用这些描述符,而后误将系统信息发送给这些描述符 open("/dev/null", O_RDONLY); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); openlog(pname, LOG_PID, facility); return (0); /* success */}守护进程在没有控制终端的环境下运行,它绝对不会收到来自内核的 SIGHUP 信号,许多守护进程因此把这个信号作为来自系统管理员的一个通知,表示其配置文件已发生改动,守护进程应该重新读取其配置文件。同理还有 SIGINT 和 SIGWINCH 等等
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
int main(int argc, char** argv) { int listenfd, connfd; socklen_t addrlen, len; struct sockaddr* cliaddr; char buff[MAXLINE]; time_t ticks; // 如果有一些显而易见的错误,则立即抛出,而不是在日志中抛出 if (argc < 2 || argc > 3) err_quit("usage: daytimetcpsrv2 [ <host> ] <service or port>"); // 使用我们的函数启动 daemon_init(argv[0], 0); if (argc == 2) listenfd = Tcp_listen(NULL, argv[1], &addrlen); else listenfd = Tcp_listen(argv[1], argv[2], &addrlen); cliaddr = Malloc(addrlen); for (;;) { len = addrlen; connfd = Accept(listenfd, cliaddr, &len); err_msg("connection from %s", Sock_ntop(cliaddr, len)); ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); Write(connfd, buff, strlen(buff)); Close(connfd); }}| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
Unix 系统中的 syslogd 守护进程通常由某个系统初始化脚本启动,并且在系统工作期间一直运行
启动的步骤:
/etc/syslog.conf ,配置了对消息的处理/var/run/log (或 /dev/log)SIGHUP 信号,则重新读取配置文件 (这让我想到了 NGINX 重载配置文件时服务无需暂停)较新的 syslogd 实现禁止创建 UDP 套接字,因为这样可能会让系统遭到 DOS 攻击,其文件系统可能被填满,从而占满内存空间,或挤掉合法的日志消息
我们一般不会直接向其发送消息,而是通过 syslog 函数写入日志!
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
守护进程记录消息可以使用 syslog 函数:
#include <syslog.h>void syslog(int priority, // 优先级 const char *message, // 消息 ...)参数解释:
%m 规范,用以输出当前 errno 值对应的错误信息
案例
syslog(LOG_INFO|LOG_LOCAL2, "rename(%s, %s): %m", file1, fil)/etc/syslog.conf
kern.* /dev/debug # 内核的所有消息发送到控制台local7.debug /var/log/cisco.log # 来自 local7 的所有消息添加到文件 cisco.log 的尾部一些细节
/var/run/log )这个套接字一直保持打开,直到进程终止openlog 和 closelog 来操作上述步骤,在首次调用 syslog 前调用 openlog,不需要发送日志时调用 closelogopenlog 和 closelog
#include <syslog.h>void openlog(const char* ident, // 日志的前缀,通常是程序名 int options, // 可选值 int facility); // 设置默认的打印设施的值void closelog(void);| options | 说明 |
|---|---|
| LOG_CONS | 若无法发送到 syslogd 则登记到控制台 |
| LOG_NDELAY | openlog 默认是懒汉加载,这个选项设置不延迟打开,立刻创建套接字 |
| LOG_PERROR | 既发送到 syslogd 守护进程,又登记到标准错误输出 |
| LOG_PID | 随每个日志消息等级进程 ID |
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
原来的系统服务模型
Unix 上的很多服务,如 SSH、FTP、Telnet 等,我们都可以把它当作一个 Server 端
在系统启动的过程中,这些进程从 /etc/rc 文件中启动,而且每个进程都执行几乎相同的启动任务:
这种方式的缺点
inted 守护进程
为了解决这种问题,4.3BSD 提供一个因特网超级服务器(即 inetd 守护进程):
daemon_init 函数的必要| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
inetd 使用前文提到的方法把自己变成一个守护进程,然后读取并处理自己的配置文件,通常在 /etc/inetd.conf ,该文件包括如下内容:
| 字段 | 说明 |
|---|---|
| service-name | 必须在 /etc/services 文件中定义 |
| socket-type | stream(TCP)、dgram(UDP) |
| protocol | 必须在 /etc/protocols 文件中定义:tcp或udp |
| wait-flag | 对于 TCP 一般为 nowait,对于 UDP 一般为 wait |
| login-name | 来自 /etc/passwd 的用户名,一般为 root |
| server-program | 调用 exec 指定的完整路径名 |
| server-program-arguments | 调用 exec 指定的命令行参数 |
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
其工作流程如下:

读入配置文件,为每个服务创建一个合适的套接字
为每个套接字调用 bind,指定端口和通配地址
端口号可以通过 getservbyname 获取,用配置文件中的 service-name 和 protocal 字段做参数
对于每个 TCP 套接字,调用 listen 以接受外来的连接请求,对于 UDP 则不执行
创建完所有套接字后,调用 select 等待其中任何一个套接字变得可读
对于 TCP:
如果设置为 wait,对于 TCP 来说,父进程会用 FD_CLR 禁止这个套接字,保存子进程 ID,当子进程终止(SIGCHILD)后才继续监听
对于 UDP:
因为数据报服务器只有一个套接字,所以只能一个一个来处理,也就是说通过接受子进程终止的 SIGCHLD 信号后才继续监听
因为假如 fork 后一个子进程,而父进程先于该子进程再次执行,则会由于还没读取数据而再次触发 select 的事件,从而再次 fork 一个无用的子进程
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
xinetd 提供与 inetd 一致的基本服务,不过还提供了数目众多的其他特性,包括根据客户的地址登记、接受或拒绝连接的选项等等
| 本文信息 | 本文信息 | 防爬虫替换信息 |
|---|---|---|
| 作者网站 | LYMTICS | https://lymtics.top |
| 作者 | LYMTICS(樵仙) | https://lymtics.top |
| 联系方式 | me@tencent.ml | me@tencent.ml |
| 原文标题 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 | 《Unix 网络编程》13:守护进程和 inet 超级服务器 |
| 原文地址 | https://www.cnblogs.com/lymtics/p/16341341.html | https://www.cnblogs.com/lymtics/p/16341341.html |
这个函数的唯一作用就是为错误处理函数设置 daemon_proc 标志,使得运行信息输出在日志中
extern int daemon_proc; /* defined in error.c */void daemon_inetd(const char* pname, int facility) { daemon_proc = 1; /* for our err_XXX() functions */ openlog(pname, LOG_PID, facility);}修改后的服务器代码:
int main(int argc, char** argv) { socklen_t len; struct sockaddr* cliaddr; char buff[MAXLINE]; time_t ticks; daemon_inetd(argv[0], 0); cliaddr = Malloc(sizeof(struct sockaddr_storage)); len = sizeof(struct sockaddr_storage); Getpeername(0, cliaddr, &len); err_msg("connection from %s", Sock_ntop(cliaddr, len)); ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); Write(0, buff, strlen(buff)); Close(0); /* close TCP connection */ exit(0);}为了启动这个程序,我们需要在 /etc/services 文件中添加:
mydaytime 9999/tcp在 /etc/inetd.conf 中添加:(在题主的电脑里就只有 xinetd.conf 了)
mydaytime stream tcp nowait andy /foo/bar/daytimetcpsrv3 daytimetcpsrv3然后给 inetd 发送一个 SIGHUP 信号,告知它重新读取配置文件