《Unix 網路編程》13:守護進程和 inetd 超級伺服器
守護進程和 inetd 超級伺服器
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
系列文章導航:《Unix 網路編程》筆記
守護進程
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
基本知識
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
守護進程的特點:
- 必須親自脫離與控制終端的關聯,從而避免與作業控制、終端會話管理、終端產生訊號等發生任何不期望的交互
- 同時要避免在後台運行的守護進程非預期地輸出到終端
守護進程的啟動
-
由系統初始化腳本啟動,通常位於
/etc/rc或/etc目錄下,由這些腳本啟動的守護進程一開始就有用超級用戶特權如:inetd 超級伺服器、Web 伺服器、sendmail 伺服器
-
由 inetd 超級伺服器啟動,它監聽網路請求
-
cron 守護進程按照規則定期執行一些程式,由它執行的程式同樣作為守護進程運行
-
at 命令用於在未來某個時刻執行一些程式
-
守護進程還可以從用戶終端或在前台或者後台啟動,這麼做往往是為了測試
daemon_init
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
- 這是我們自己寫的一個函數,把一個普通進程轉變為守護進程
- 部分 Unix 變體上提供一個名為 daemon 的 C 庫函數,實現類似的功能
由於這段程式碼涉及的知識點比較多,所以首先需要補充一點作業系統的知識:Linux 進程、進程組、會話周期、控制終端
按照上文中的描述閱讀下述程式碼:
- 在父進程(此時是一個進程組的組長)中使用fork()產生子進程(將來的守護進程由它產生)
- 調用setsid(),用於生成一個新的會話 注意如果當前進程是會話組長時,調用失敗。第一點已經可以保證進程不是會話組長了,所以setsid()調用成功後,進程成為新的會話組長和新的進程組長,並與原來的登錄會話和進程組脫離。由於會話對控制終端的獨佔性,進程同時與控制終端脫離
- 禁止進程重新打開控制終端 第二步之後,進程已經成為無終端的會話組長。但它可以重新申請打開一個控制終端。可以通過使進程不再成為會話組長來禁止進程重新打開控制終端,在上面的控制終端中已經提到了只有會話組長才能打開控制終端;
#include <syslog.h>
#include "unp.h"
#define MAXFD 64
extern 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 | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //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 | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
syslogd
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
Unix 系統中的 syslogd 守護進程通常由某個系統初始化腳本啟動,並且在系統工作期間一直運行
啟動的步驟:
- 讀取配置:通常為
/etc/syslog.conf,配置了對消息的處理 - 創建Unix域數據報套接字:給他綁定路徑名
/var/run/log(或/dev/log) - 創建一個 UDP 套接字:綁定埠 514
- 打開路徑名 /dev/klog:來自內核中的任何出錯消息從這個「設備」輸入
- 監聽:調用 select,監聽 2 ~ 4 步驟的描述符
- 如果收到消息,讀入並按照配置進行處理
- 如果收到
SIGHUP訊號,則重新讀取配置文件 (這讓我想到了 NGINX 重載配置文件時服務無需暫停)
較新的 syslogd 實現禁止創建 UDP 套接字,因為這樣可能會讓系統遭到 DOS 攻擊,其文件系統可能被填滿,從而佔滿記憶體空間,或擠掉合法的日誌消息
我們一般不會直接向其發送消息,而是通過 syslog 函數寫入日誌!
syslog
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
守護進程記錄消息可以使用 syslog 函數:
#include <syslog.h>
void syslog(int priority, // 優先順序
const char *message, // 消息
...)
參數解釋:
- priority:好理解,大多日誌都有日誌級別的功能
- facility:用於標識發送進程類型(函數的參數列表中沒有?因為這個要和 priority 用邏輯或使用)
- message:消息內容,類似 printf,不同的是其增加了
%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 的尾部
一些細節
- 當 syslog 被應用程式首次調用時,它創建一個 Unix 域數據報套接字,然後調用 connect 連接到由 syslogd 守護進程創建的 Unix 域數據報套接字的眾所周知路徑名(如
/var/run/log)這個套接字一直保持打開,直到進程終止 - 可以用
openlog和closelog來操作上述步驟,在首次調用 syslog 前調用 openlog,不需要發送日誌時調用 closelog
openlog 和 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 |
inetd 守護進程
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
引入 inetd
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
原來的系統服務模型
Unix 上的很多服務,如 SSH、FTP、Telnet 等,我們都可以把它當作一個 Server 端
在系統啟動的過程中,這些進程從 /etc/rc 文件中啟動,而且每個進程都執行幾乎相同的啟動任務:
- 創建一個套接字,綁定埠
- 等待連接或數據報的到來
- 派生子進程,子進程提供服務,父進程繼續監聽
這種方式的缺點
- 所有這些守護進程含有幾乎相同的啟動程式碼
- 每個守護進程在進程表中佔據一個表項,然而大多數時間他們處於睡眠狀態
inted 守護進程
為了解決這種問題,4.3BSD 提供一個網際網路超級伺服器(即 inetd 守護進程):
- 通過由 inetd 處理普通守護進程的大部分啟動細節以簡化守護進程的編寫,這麼一來每個伺服器不再有調用
daemon_init函數的必要 - 單個進程(inetd)就能為多個服務等待外來的客戶請求,以取代每個伺服器一個進程的做法
inetd 配置文件
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //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 指定的命令行參數 |
inetd 工作流程
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
其工作流程如下:

-
讀入配置文件,為每個服務創建一個合適的套接字
-
為每個套接字調用 bind,指定埠和通配地址
埠號可以通過 getservbyname 獲取,用配置文件中的 service-name 和 protocal 欄位做參數
-
對於每個 TCP 套接字,調用 listen 以接受外來的連接請求,對於 UDP 則不執行
-
創建完所有套接字後,調用 select 等待其中任何一個套接字變得可讀
對於 TCP:
- 當 select 返回可讀後,如果該套接字是一個 TCP 套接字,而且其 wait-flag 為 nowait,則調用 accept 接受這個新連接
- inetd 守護進程調用 fork 派生進程,並由子進程處理服務請求,與前面的並發伺服器類似,關閉相應的描述符
- 子進程關閉除了要處理的套接字描述符外的所有的描述符,並根據配置文件,切換到相應的用戶,用 exec 執行相應的操作
如果設置為 wait,對於 TCP 來說,父進程會用 FD_CLR 禁止這個套接字,保存子進程 ID,當子進程終止(SIGCHILD)後才繼續監聽
對於 UDP:
因為數據報伺服器只有一個套接字,所以只能一個一個來處理,也就是說通過接受子進程終止的 SIGCHLD 訊號後才繼續監聽
因為假如 fork 後一個子進程,而父進程先於該子進程再次執行,則會由於還沒讀取數據而再次觸發 select 的事件,從而再次 fork 一個無用的子進程
xinetd
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //www.cnblogs.com/lymtics/p/16341341.html |
- 如果您訪問本文的鏈接並非如上地址,則可能是爬取作者的文章,建議返回原站閱讀,謝謝您的支援
- 原文會不斷地更新和完善,排版和樣式會更加適合閱讀,並且有相關配圖
xinetd 提供與 inetd 一致的基本服務,不過還提供了數目眾多的其他特性,包括根據客戶的地址登記、接受或拒絕連接的選項等等
daemond_inetd
| 本文資訊 | 本文資訊 | 防爬蟲替換資訊 |
|---|---|---|
| 作者網站 | LYMTICS | //lymtics.top |
| 作者 | LYMTICS(樵仙) | //lymtics.top |
| 聯繫方式 | [email protected] | [email protected] |
| 原文標題 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 | 《Unix 網路編程》13:守護進程和 inet 超級伺服器 |
| 原文地址 | //www.cnblogs.com/lymtics/p/16341341.html | //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);
}
- 我們用描述符 0 指代被接受的 TCP 連接
- 因為這個程式針對每一個連接啟動一次,所以不需要無限循環
為了啟動這個程式,我們需要在 /etc/services 文件中添加:
mydaytime 9999/tcp
在 /etc/inetd.conf 中添加:(在題主的電腦里就只有 xinetd.conf 了)
mydaytime stream tcp nowait andy /foo/bar/daytimetcpsrv3 daytimetcpsrv3
然後給 inetd 發送一個 SIGHUP 訊號,告知它重新讀取配置文件

