電子產(chǎn)業(yè)一站式賦能平臺

PCB聯(lián)盟網(wǎng)

搜索
查看: 71|回復(fù): 0
收起左側(cè)

Linux守護進程

[復(fù)制鏈接]

660

主題

660

帖子

4567

積分

四級會員

Rank: 4

積分
4567
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-11-25 08:03:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
7 b" f+ O6 a1 m% @: F( {
點擊上方藍色字體,關(guān)注我們+ e, U$ u9 c! z$ w+ X7 `% k$ H

  T$ ^* o( a4 M4 T5 n在命令輸出中,如果 TTY 一欄顯示為問號(?),這表示該進程沒有控制終端,通常意味著它是一個守護進程。同時,COMMAND 一欄中用中括號([])括起來的進程表示內(nèi)核線程。+ ?7 X. j- W0 V

! w! q% X- I) P  x6 |" l' T( B這些線程是在內(nèi)核空間中創(chuàng)建的,沒有對應(yīng)的用戶空間代碼,因此不具備程序文件名和命令行信息,通常以字母 k 開頭,表示它們是內(nèi)核線程(Kernel)。$ N1 ?+ b3 _/ I2 O+ G
12 P4 Q! N# _9 e! g. P8 z" \- p
編寫守護進程的步驟
$ H; G) z" L# o$ f9 P" f$ B, c8 y編寫守護進程通常包括以下幾個關(guān)鍵步驟,以確保其能夠在后臺獨立運行,并完成預(yù)定的任務(wù)。
; `: y( ]" t- P3 l* W% i8 U3 n0 a: g
1、創(chuàng)建子進程并終止父進程
# B. T. U2 S7 \/ m" |" O: t( @9 V使用 fork() 創(chuàng)建子進程后,父進程應(yīng)調(diào)用 exit() 終止自身。這一過程實現(xiàn)了以下幾點:' j" D% b8 L8 D# V  S4 r( Q2 j
  • 如果守護進程是通過簡單的 shell 命令啟動,父進程的退出將使 shell 認為命令已執(zhí)行完畢。
  • 子進程繼承了父進程的進程組 ID,但它有自己獨立的進程 ID,確保子進程不是進程組的組長,為后續(xù)調(diào)用 setsid() 準(zhǔn)備條件。
    9 T4 \: j3 Y( o/ Y2 W
    9 m+ {( r) ?1 r) H
    2、子進程調(diào)用 setsid() 創(chuàng)建會話
    9 @7 M4 u; D8 T4 x在子進程中調(diào)用 setsid() 是關(guān)鍵步驟。這將:
    % A9 K. v9 e1 s/ A
  • 創(chuàng)建一個新的會話,子進程成為新會話的首領(lǐng)。
  • 創(chuàng)建新的進程組,子進程成為組長。
  • 擺脫原有會話、進程組和控制終端的控制,實現(xiàn)完全獨立。& h0 a& ?$ V0 D  n  I$ X% ?
    盡管子進程在 fork() 時繼承了父進程的控制權(quán),但 setsid() 能確保其完全脫離。$ V# u; A' ~2 ]$ y

    * e: c/ C6 a0 r! J3、更改工作目錄為根目錄% Q1 o* F* i/ m5 y! H# l0 K
    子進程會繼承父進程的當(dāng)前工作目錄,而該目錄可能會導(dǎo)致文件系統(tǒng)無法卸載。通常,守護進程會將工作目錄更改為根目錄(/),以避免這種問題。也可以根據(jù)需要選擇其他目錄。
    : j: }* m% j! G+ I1 N5 Q3 Z0 R. f* J* [# P9 l" o& I" w* z+ D
    4、重設(shè)文件權(quán)限掩碼(umask)
    6 F% U. N& H& b% S$ O5 k1 H4 g文件權(quán)限掩碼 umask 控制新建文件的默認權(quán)限。由于子進程繼承了父進程的 umask,建議將其設(shè)置為 0,以確保子進程擁有最大權(quán)限,增強守護進程的靈活性。設(shè)置 umask 的方法是調(diào)用 umask(0)。
    7 o& q) }9 g" k
    2 f2 w4 v( v+ h' [' X; p; n5、關(guān)閉不再需要的文件描述符
    6 h  k" w5 T% d1 R6 T, h( a, m( [8 v子進程會繼承父進程打開的所有文件描述符,這可能導(dǎo)致不必要的資源消耗。應(yīng)關(guān)閉不再需要的文件描述符,以確保守護進程不再持有任何繼承自父進程的描述符,從而減少資源浪費。
    0 D7 m7 W0 W$ z6 ~
    # j% T* {/ g# O) ]% J6、將文件描述符 0、1、2 定位到 /dev/null6 Y4 O3 T; S, K+ J7 P) C! S
    守護進程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤通常會重定向到 /dev/null,這樣守護進程的輸出就不會顯示在任何地方,同時也不會試圖從交互式用戶那里接收輸入。
    " Y/ X3 U( s6 h( i
    6 l& R7 K. ~/ F3 o) r  R1 r7 [, u7、其他處理:忽略 SIGCHLD 信號
    2 l3 v* J$ O5 \' t) g  Z處理 SIGCHLD 信號不是絕對必要的,但對于某些并發(fā)服務(wù)器進程尤其重要。通過將 SIGCHLD 信號的處理方式設(shè)置為 SIG_IGN,可以避免僵尸進程的產(chǎn)生。這樣,當(dāng)子進程結(jié)束時,內(nèi)核將其交給 init 進程處理,減少了父進程的負擔(dān),從而提高了服務(wù)器的并發(fā)性能。
    1 x. ^+ C# d8 f5 |  @. f1 T2
    4 ~' H- P+ F. ~5 `* _守護進程的使用和案例設(shè)計" _/ _6 u2 S  T
    為了深入理解如何創(chuàng)建和使用守護進程,我們將創(chuàng)建一個多功能的守護進程,具備以下功能:: L' o7 E" c" b) E# |+ R" p
  • 資源監(jiān)控功能:守護進程每隔 30 秒獲取系統(tǒng)的 CPU、內(nèi)存和磁盤使用信息,并將其寫入 /var/log/resource_monitor.log。
  • 定時清理功能:每隔 10 分鐘,清理 /tmp 目錄下的所有文件。
  • 信號處理功能:守護進程能夠捕獲 SIGTERM 信號,安全退出,并能夠處理 SIGHUP 信號重新加載配置文件。
    * x: n- L7 l. c& b2 B
    + T9 {1 @. K) X! }; p. P% s
    2.1、案例功能分析
    ; o5 R5 r4 @. t% i! K5 R+ ^系統(tǒng)資源監(jiān)控% m' l$ t1 o! p! D" a
  • 使用系統(tǒng)命令 stat 和 vmstat 來獲取 CPU 和內(nèi)存信息。
  • 使用 df 命令獲取磁盤使用情況。
  • 每次獲取的信息都寫入 /var/log/resource_monitor.log,便于運維人員檢查系統(tǒng)的健康狀態(tài)。# S2 r3 U5 y4 Q3 f, G& H9 w
    5 {# Z" V& f9 m- b: s# L* P
    定時清理任務(wù)
    5 E8 }5 n! o  T, g; u8 N
  • 每隔 10 分鐘調(diào)用一個函數(shù)清理 /tmp 目錄下的文件。
  • 使用系統(tǒng)函數(shù) unlink() 刪除文件。/ }7 ]5 `$ ?' a2 Y" b& r8 O

    3 _& w2 s5 V, _( X* |6 \信號處理, l8 S) T7 }3 k  o4 a- i% ^
  • 捕獲 SIGTERM 信號,干凈地終止守護進程并進行資源釋放。
  • 捕獲 SIGHUP 信號,重新加載配置文件(如改變?nèi)罩疚募穆窂剑?br /> $ [' ?3 ?4 `9 B& s. M8 t6 D) P' J' g
    8 [* W4 f% a1 K2.2、守護進程代碼結(jié)構(gòu)
      H* d7 V; m% G0 v; N; j1 z" P
  • daemonize():負責(zé)將進程變?yōu)槭刈o進程的常規(guī)步驟。
  • monitor_resources():負責(zé)監(jiān)控系統(tǒng)資源并將其寫入日志。
  • cleanup_tmp():每隔 10 分鐘清理一次 /tmp 目錄中的文件。
  • handle_signal():處理 SIGTERM 和 SIGHUP 信號。
  • reload_config():當(dāng)捕獲 SIGHUP 時,重新加載配置文件。0 O  [1 J. P+ R* \
    6 C/ i2 f" {( U/ g( U' v) b
    2.3、代碼實現(xiàn)0 Z" ~+ k5 N- y2 @
  • #define LOG_FILE "/var/log/resource_monitor.log"#define CONFIG_FILE "/etc/daemon_config.conf"#define TMP_DIR "/tmp"
    # m. M% p5 b# T. u6 g// 定義輪詢時間#define MONITOR_INTERVAL 30  // 資源監(jiān)控間隔 30 秒#define CLEANUP_INTERVAL 600 // 清理間隔 10 分鐘
    : k2 s# R7 n9 b2 r) `" ~* Nint keep_running = 1;FILE *log_fp = NULL;
    0 W7 t. Q. f# x/ O; `* ^// 守護進程初始化函數(shù)void daemonize() {    pid_t pid;
    1 ]+ V& w/ k/ O- Z( _    // 1. 創(chuàng)建子進程并終止父進程    pid = fork();    if (pid 0) exit(EXIT_FAILURE);    if (pid > 0) exit(EXIT_SUCCESS);  // 父進程退出# m* t  g) L! q& L6 e1 H7 ^
        // 2. 創(chuàng)建新的會話    if (setsid() 0) exit(EXIT_FAILURE);
    : R- y1 x. U1 l4 z9 k% e$ ]    // 3. 忽略 SIGCHLD 信號    signal(SIGCHLD, SIG_IGN);% Z; x# y  U+ r8 \  I2 b. G7 B
        // 4. 再次 fork,防止守護進程重新獲得終端    pid = fork();    if (pid 0) exit(EXIT_FAILURE);    if (pid > 0) exit(EXIT_SUCCESS);
    : _0 O) k: W% U6 v0 V( C    // 5. 更改工作目錄到根目錄    chdir("/");
    4 W7 K  b9 X2 p# x8 ~    // 6. 重設(shè)文件權(quán)限掩碼    umask(0);* r. k8 b# ^: `) J
        // 7. 關(guān)閉不再需要的文件描述符    close(STDIN_FILENO);    close(STDOUT_FILENO);    close(STDERR_FILENO);
    4 M# i5 P8 x% V1 a* U, u2 P4 E4 ^    // 8. 重定向標(biāo)準(zhǔn)輸入、輸出、錯誤到 /dev/null    open("/dev/null", O_RDONLY);    open("/dev/null", O_WRONLY);    open("/dev/null", O_WRONLY);+ y! j  ]$ u+ |  P$ L
        // 打開系統(tǒng)日志    openlog("resource_daemon", LOG_PID, LOG_DAEMON);}
    - Z' @' @" j1 A. F1 A1 C// 捕獲信號的處理函數(shù)void handle_signal(int signal) {    switch (signal) {        case SIGHUP:            syslog(LOG_INFO, "Reloading configuration file...");            // 重新加載配置文件            if (log_fp) {                fclose(log_fp);            }            log_fp = fopen(LOG_FILE, "a");            if (log_fp == NULL) {                syslog(LOG_ERR, "Failed to open log file");                exit(EXIT_FAILURE);            }            break;        case SIGTERM:            syslog(LOG_INFO, "Daemon is shutting down...");            if (log_fp) {                fclose(log_fp);            }            closelog();            keep_running = 0;  // 設(shè)置標(biāo)志位,結(jié)束主循環(huán)            break;    }}
    1 r/ c& U) I2 y2 C// 資源監(jiān)控功能void monitor_resources() {    FILE *fp;    char buffer[128];
    8 r) E: A" k1 ]/ g' c0 a$ j    // 記錄當(dāng)前時間    time_t now = time(NULL);    fprintf(log_fp, "Timestamp: %s", ctime(&now));9 |. d2 p* c- P! r# v0 T( v$ q
        // 記錄 CPU 和內(nèi)存使用情況    fp = popen("vmstat 1 2 | tail -1", "r");    if (fp != NULL) {        fgets(buffer, sizeof(buffer) - 1, fp);        fprintf(log_fp, "CPU/Memory Usage: %s3 D6 u  d0 ]6 b: {( a8 a
    ", buffer);        pclose(fp);    }
    & A8 U' t' a) p2 P6 d4 A5 o    // 記錄磁盤使用情況    fp = popen("df -h /", "r");    if (fp != NULL) {        while (fgets(buffer, sizeof(buffer) - 1, fp) != NULL) {            fprintf(log_fp, "Disk Usage: %s", buffer);        }        pclose(fp);    }9 }) T5 z" |4 R3 d
        fflush(log_fp);  // 確保日志刷新到文件}: W9 `5 r- y# Y7 a9 X- ]$ ?/ u6 {
    // 定時清理 /tmp 目錄void cleanup_tmp() {    DIR *dir;    struct dirent *entry;    char file_path[256];
    $ @& \; Z+ ^9 A5 Q    dir = opendir(TMP_DIR);    if (dir == NULL) {        syslog(LOG_ERR, "Failed to open /tmp directory");        return;    }
    & |' Q& Y% N5 b    while ((entry = readdir(dir)) != NULL) {        if (entry->d_type == DT_REG) {  // 只刪除常規(guī)文件            snprintf(file_path, sizeof(file_path), "%s/%s", TMP_DIR, entry->d_name);            if (unlink(file_path) == 0) {                syslog(LOG_INFO, "Deleted file: %s", file_path);            } else {                syslog(LOG_ERR, "Failed to delete file: %s", file_path);            }        }    }
    1 @  T' ]% e8 y5 H4 q    closedir(dir);}+ a1 h1 M6 A2 o" |# I
    int main() {    daemonize();' M7 L  o# d$ a. R6 K  d( m
        // 打開日志文件    log_fp = fopen(LOG_FILE, "a");    if (log_fp == NULL) {        syslog(LOG_ERR, "Failed to open log file");        exit(EXIT_FAILURE);    }1 ]1 t, `4 p% ]3 x6 g
        // 捕獲信號處理    signal(SIGTERM, handle_signal);  // 用于進程關(guān)閉    signal(SIGHUP, handle_signal);   // 用于重新加載配置' T& r# L$ ~) y  x  @6 q- g9 Z6 l
        time_t last_cleanup = time(NULL);- @: F( g( o) {. H
        // 主循環(huán)    while (keep_running) {        monitor_resources();  // 監(jiān)控系統(tǒng)資源, Q2 l8 `+ J' k
            // 檢查是否需要清理 tmp 目錄        if (difftime(time(NULL), last_cleanup) >= CLEANUP_INTERVAL) {            cleanup_tmp();            last_cleanup = time(NULL);        }
    $ I5 P2 R7 g4 M( ~9 |1 v4 b: O+ A% v        // 等待 30 秒后繼續(xù)        sleep(MONITOR_INTERVAL);    }
    6 M& ~: w( R" |; E) l: G: @    // 清理資源并退出    if (log_fp) {        fclose(log_fp);    }    closelog();
    0 t, }1 X) i8 n' t- s    return 0;}& L# F1 C3 r3 O) O* X
    2.4、代碼詳解4 s1 r+ g8 ?6 O8 C
    守護進程初始化 (daemonize)
    " _1 A* z+ }/ a2 F8 j2 S+ V
  • 將進程變?yōu)槭刈o進程,使用了雙 fork() 技術(shù),確保進程在后臺運行并與終端脫離關(guān)系。
  • 使用 syslog 系統(tǒng)日志服務(wù)記錄進程啟動、關(guān)閉等信息。, x% t- G8 o( R  S

    % |9 C" z% F& G0 w信號處理 (handle_signal)
    , V; r. ~$ w9 P) j1 g/ n# }
  • 通過 signal() 函數(shù)捕獲 SIGTERM 和 SIGHUP 信號。
  • SIGTERM 信號用于干凈地終止守護進程。
  • SIGHUP 信號用于重新加載配置文件,這里模擬了重新打開日志文件的過程。
    . H1 e8 Z6 {+ `0 ^

    6 ~) C$ e3 U$ E4 ~* |7 n資源監(jiān)控 (monitor_resources)( F2 l5 P) d; W
  • 使用 vmstat 命令監(jiān)控 CPU 和內(nèi)存使用情況,df 命令獲取磁盤使用狀態(tài)。
  • 每次監(jiān)控結(jié)果都記錄到日志文件中。
    2 O, N* Q0 E! O7 ^* X

    " F' I! s! i0 V( m: j- G定時清理 (cleanup_tmp)  e3 N7 ^/ p* t- w0 g
  • 每隔 10 分鐘清理 /tmp 目錄下的文件。
  • 僅刪除常規(guī)文件,忽略目錄等。+ `! P! \! Z$ r9 a: g

    * T" `  p0 y" S7 u& e8 F, _主循環(huán)0 g2 B8 p% _& ~  N9 V" d
  • 守護進程每 30 秒調(diào)用監(jiān)控和清理函數(shù),保持持續(xù)運行狀態(tài)。5 Q7 O* l7 d) }. I
    & Y) d7 R+ Z/ m
    37 w+ F) m" g0 s( g- o4 [( G
    編譯和運行守護進程0 F; v* ]9 y2 W; ?. E
    將上述代碼保存為 resource_monitor.c,使用以下命令進行編譯和運行:9 D( D& }% j$ P% m- ~) L$ `  P

    5 k* ^: a/ ]/ N9 M' t, b/ q! @
  • gcc resource_monitor.c -o resource_monitorsudo ./resource_monitor
    & H# q" {9 w# ^  W) z注意,守護進程需要寫入 /var/log/resource_monitor.log 文件,因此需要使用 sudo 權(quán)限運行。* \' F$ S/ R% ~. ]
    4' {7 L+ B5 b4 p$ j# f! o* q
    檢查守護進程8 d, g+ ^$ |. p( |: k: Z3 X
    查看日志文件內(nèi)容:
    3 y5 B- J  w' m1 `+ T$ J1 ]1 Q4 m
  • cat /var/log/resource_monitor.log5 Y" K) ^7 K5 t2 z6 s# Q
    查看守護進程狀態(tài):1 A. j2 ?+ a5 e, I. ~) m  O! N+ D

    : @7 Q2 f( z/ M: f; ~1 w! |% p
  • ps -ef | grep resource_monitor5 \" L  k/ }8 O. f& @, l
    可以使用 kill 命令根據(jù)守護進程的 PID 將其終止:
    0 j& n9 @( `- [7 R, y# q3 k- C
    6 u* t9 x- y3 k2 ?6 r% Z
  • kill; u* [1 _& y  B$ c* Z
    8 C6 Y. r7 \, G' r$ ]# I  e; k
    ' O6 E! A; t7 E; q
    點擊閱讀原文,更精彩~
  • 回復(fù)

    使用道具 舉報

    發(fā)表回復(fù)

    您需要登錄后才可以回帖 登錄 | 立即注冊

    本版積分規(guī)則


    聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表