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

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

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

Linux守護(hù)進(jìn)程

[復(fù)制鏈接]

660

主題

660

帖子

4567

積分

四級(jí)會(huì)員

Rank: 4

積分
4567
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-11-25 08:03:00 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式

. ]- d1 d$ k1 b) L5 a# Q+ L- w點(diǎn)擊上方藍(lán)色字體,關(guān)注我們0 p# E5 A+ d2 \
8 m8 \+ o% @% e
在命令輸出中,如果 TTY 一欄顯示為問(wèn)號(hào)(?),這表示該進(jìn)程沒(méi)有控制終端,通常意味著它是一個(gè)守護(hù)進(jìn)程。同時(shí),COMMAND 一欄中用中括號(hào)([])括起來(lái)的進(jìn)程表示內(nèi)核線程。8 J1 f7 Y! T  P% }

; p% m; [4 R/ w2 I& h這些線程是在內(nèi)核空間中創(chuàng)建的,沒(méi)有對(duì)應(yīng)的用戶空間代碼,因此不具備程序文件名和命令行信息,通常以字母 k 開(kāi)頭,表示它們是內(nèi)核線程(Kernel)。2 C+ T2 n3 Q: }' \6 Z, H4 {0 i
14 U$ M: e. J9 b3 b
編寫(xiě)守護(hù)進(jìn)程的步驟% f' W  y1 }+ |) D
編寫(xiě)守護(hù)進(jìn)程通常包括以下幾個(gè)關(guān)鍵步驟,以確保其能夠在后臺(tái)獨(dú)立運(yùn)行,并完成預(yù)定的任務(wù)。
) U: T  R6 H! ]' Y2 W3 b3 c% g* D+ X% s: [, S5 r  i  n
1、創(chuàng)建子進(jìn)程并終止父進(jìn)程$ h/ b$ @5 ~( o% D( C- j
使用 fork() 創(chuàng)建子進(jìn)程后,父進(jìn)程應(yīng)調(diào)用 exit() 終止自身。這一過(guò)程實(shí)現(xiàn)了以下幾點(diǎn):
0 d6 X6 B; s/ p+ N; Y% ~% m
  • 如果守護(hù)進(jìn)程是通過(guò)簡(jiǎn)單的 shell 命令啟動(dòng),父進(jìn)程的退出將使 shell 認(rèn)為命令已執(zhí)行完畢。
  • 子進(jìn)程繼承了父進(jìn)程的進(jìn)程組 ID,但它有自己獨(dú)立的進(jìn)程 ID,確保子進(jìn)程不是進(jìn)程組的組長(zhǎng),為后續(xù)調(diào)用 setsid() 準(zhǔn)備條件。
    , w* Y# s! _7 o* ~* o
    " h8 X2 q  P! P% t0 `
    2、子進(jìn)程調(diào)用 setsid() 創(chuàng)建會(huì)話/ w7 p0 B; f9 m/ Q2 J0 Z# h9 l
    在子進(jìn)程中調(diào)用 setsid() 是關(guān)鍵步驟。這將:
    : Q' y  m* k  b  {
  • 創(chuàng)建一個(gè)新的會(huì)話,子進(jìn)程成為新會(huì)話的首領(lǐng)。
  • 創(chuàng)建新的進(jìn)程組,子進(jìn)程成為組長(zhǎng)。
  • 擺脫原有會(huì)話、進(jìn)程組和控制終端的控制,實(shí)現(xiàn)完全獨(dú)立。+ R% v( K- b* j: Q( F3 X: W
    盡管子進(jìn)程在 fork() 時(shí)繼承了父進(jìn)程的控制權(quán),但 setsid() 能確保其完全脫離。
    ) t6 p6 L, W& m3 l

    5 t5 E8 H3 g) O; ~# G% y5 ^/ ]3、更改工作目錄為根目錄" e  c' i4 `3 ~
    子進(jìn)程會(huì)繼承父進(jìn)程的當(dāng)前工作目錄,而該目錄可能會(huì)導(dǎo)致文件系統(tǒng)無(wú)法卸載。通常,守護(hù)進(jìn)程會(huì)將工作目錄更改為根目錄(/),以避免這種問(wèn)題。也可以根據(jù)需要選擇其他目錄。
    3 q/ L/ i/ P4 ~& _6 Q+ P
    : U; c3 ]' E2 \. U4、重設(shè)文件權(quán)限掩碼(umask)
    + ]& L7 r0 A- G文件權(quán)限掩碼 umask 控制新建文件的默認(rèn)權(quán)限。由于子進(jìn)程繼承了父進(jìn)程的 umask,建議將其設(shè)置為 0,以確保子進(jìn)程擁有最大權(quán)限,增強(qiáng)守護(hù)進(jìn)程的靈活性。設(shè)置 umask 的方法是調(diào)用 umask(0)。5 F5 w7 I0 J* X( _$ N8 i/ J' f  R; B

    9 q" ?* h/ E8 |, W* W5、關(guān)閉不再需要的文件描述符
    + W7 z& O, ?5 d1 `子進(jìn)程會(huì)繼承父進(jìn)程打開(kāi)的所有文件描述符,這可能導(dǎo)致不必要的資源消耗。應(yīng)關(guān)閉不再需要的文件描述符,以確保守護(hù)進(jìn)程不再持有任何繼承自父進(jìn)程的描述符,從而減少資源浪費(fèi)。1 A; y1 ]- M; l* `0 ]7 u
    ) d( K- ^) d/ o' q9 \: }& J
    6、將文件描述符 0、1、2 定位到 /dev/null1 o: B# y& X: K5 L. A
    守護(hù)進(jìn)程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤通常會(huì)重定向到 /dev/null,這樣守護(hù)進(jìn)程的輸出就不會(huì)顯示在任何地方,同時(shí)也不會(huì)試圖從交互式用戶那里接收輸入。
    . I/ X1 K& d" I: [9 j( _- e! @- Y, i& l9 t4 R9 ~& Z
    7、其他處理:忽略 SIGCHLD 信號(hào)
    0 T4 [& n* z' F: a處理 SIGCHLD 信號(hào)不是絕對(duì)必要的,但對(duì)于某些并發(fā)服務(wù)器進(jìn)程尤其重要。通過(guò)將 SIGCHLD 信號(hào)的處理方式設(shè)置為 SIG_IGN,可以避免僵尸進(jìn)程的產(chǎn)生。這樣,當(dāng)子進(jìn)程結(jié)束時(shí),內(nèi)核將其交給 init 進(jìn)程處理,減少了父進(jìn)程的負(fù)擔(dān),從而提高了服務(wù)器的并發(fā)性能。
    ! W# r. d( {5 M1 Z+ p2
    & R, E% ?' `0 [% ~$ p' A# v) Y守護(hù)進(jìn)程的使用和案例設(shè)計(jì)
    / U8 }! z4 u% j. K% ^0 O為了深入理解如何創(chuàng)建和使用守護(hù)進(jìn)程,我們將創(chuàng)建一個(gè)多功能的守護(hù)進(jìn)程,具備以下功能:
    4 i1 i; r+ E) @- `
  • 資源監(jiān)控功能:守護(hù)進(jìn)程每隔 30 秒獲取系統(tǒng)的 CPU、內(nèi)存和磁盤(pán)使用信息,并將其寫(xiě)入 /var/log/resource_monitor.log。
  • 定時(shí)清理功能:每隔 10 分鐘,清理 /tmp 目錄下的所有文件。
  • 信號(hào)處理功能:守護(hù)進(jìn)程能夠捕獲 SIGTERM 信號(hào),安全退出,并能夠處理 SIGHUP 信號(hào)重新加載配置文件。
    0 e2 K( j9 ~( E" ?+ ]

    4 O( U9 W7 g8 ?! g5 M1 A; L, A3 O2.1、案例功能分析5 _6 u- Q) e# }2 b
    系統(tǒng)資源監(jiān)控
    % H4 a" t) W' ]" u0 w7 z
  • 使用系統(tǒng)命令 stat 和 vmstat 來(lái)獲取 CPU 和內(nèi)存信息。
  • 使用 df 命令獲取磁盤(pán)使用情況。
  • 每次獲取的信息都寫(xiě)入 /var/log/resource_monitor.log,便于運(yùn)維人員檢查系統(tǒng)的健康狀態(tài)。
    . m% @' c/ w: ]
    . o3 M# y2 c) v' n5 t6 D
    定時(shí)清理任務(wù)# X7 c8 l; q- }  T( N
  • 每隔 10 分鐘調(diào)用一個(gè)函數(shù)清理 /tmp 目錄下的文件。
  • 使用系統(tǒng)函數(shù) unlink() 刪除文件。4 ^" p' E% e6 I

      D) j6 ^0 f* T8 }7 A6 \信號(hào)處理1 e+ V2 U" V7 P  a
  • 捕獲 SIGTERM 信號(hào),干凈地終止守護(hù)進(jìn)程并進(jìn)行資源釋放。
  • 捕獲 SIGHUP 信號(hào),重新加載配置文件(如改變?nèi)罩疚募穆窂剑?font class="jammer">2 \+ w" ]$ S, t

    , c7 ]+ k- a( N) i7 S% j7 S$ w2.2、守護(hù)進(jìn)程代碼結(jié)構(gòu)
    & c3 k" B1 ]0 o$ r, ~
  • daemonize():負(fù)責(zé)將進(jìn)程變?yōu)槭刈o(hù)進(jìn)程的常規(guī)步驟。
  • monitor_resources():負(fù)責(zé)監(jiān)控系統(tǒng)資源并將其寫(xiě)入日志。
  • cleanup_tmp():每隔 10 分鐘清理一次 /tmp 目錄中的文件。
  • handle_signal():處理 SIGTERM 和 SIGHUP 信號(hào)。
  • reload_config():當(dāng)捕獲 SIGHUP 時(shí),重新加載配置文件。: j5 Y* C4 k# L, h! m4 i; C6 G6 ?
    7 t% p" W/ w$ d  A
    2.3、代碼實(shí)現(xiàn), R% _2 p0 b& t8 o0 C0 g. x
  • #define LOG_FILE "/var/log/resource_monitor.log"#define CONFIG_FILE "/etc/daemon_config.conf"#define TMP_DIR "/tmp"
      n6 V/ R" D- p9 o) `// 定義輪詢時(shí)間#define MONITOR_INTERVAL 30  // 資源監(jiān)控間隔 30 秒#define CLEANUP_INTERVAL 600 // 清理間隔 10 分鐘& M1 W$ z( c6 V4 Y
    int keep_running = 1;FILE *log_fp = NULL;/ w8 |6 C8 S9 \$ P# [. _* k2 {
    // 守護(hù)進(jìn)程初始化函數(shù)void daemonize() {    pid_t pid;' n$ \  C: a# w  _# g; Y1 U% k
        // 1. 創(chuàng)建子進(jìn)程并終止父進(jìn)程    pid = fork();    if (pid 0) exit(EXIT_FAILURE);    if (pid > 0) exit(EXIT_SUCCESS);  // 父進(jìn)程退出
    ! p5 t: H; D; ~: J+ ?    // 2. 創(chuàng)建新的會(huì)話    if (setsid() 0) exit(EXIT_FAILURE);- W, X' Q8 V5 K% l- O
        // 3. 忽略 SIGCHLD 信號(hào)    signal(SIGCHLD, SIG_IGN);8 g* s! ^4 N* r4 ~: T
        // 4. 再次 fork,防止守護(hù)進(jìn)程重新獲得終端    pid = fork();    if (pid 0) exit(EXIT_FAILURE);    if (pid > 0) exit(EXIT_SUCCESS);
    4 T& i4 J& X6 I; A; \" I; k! U    // 5. 更改工作目錄到根目錄    chdir("/");% ?0 ?, z7 S1 p9 y. B3 c! a) t  w7 _
        // 6. 重設(shè)文件權(quán)限掩碼    umask(0);4 p7 g0 E* B& E. L( e2 w% w9 G
        // 7. 關(guān)閉不再需要的文件描述符    close(STDIN_FILENO);    close(STDOUT_FILENO);    close(STDERR_FILENO);
    " B+ Y: ~/ h$ D5 K0 {2 `    // 8. 重定向標(biāo)準(zhǔn)輸入、輸出、錯(cuò)誤到 /dev/null    open("/dev/null", O_RDONLY);    open("/dev/null", O_WRONLY);    open("/dev/null", O_WRONLY);( Z" \5 O4 ^3 o$ |8 V3 v) E  O
        // 打開(kāi)系統(tǒng)日志    openlog("resource_daemon", LOG_PID, LOG_DAEMON);}4 H6 H- G: c! ^- m- ]' s% `' |, a
    // 捕獲信號(hào)的處理函數(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;    }}+ c+ i5 m# L* O3 H7 D
    // 資源監(jiān)控功能void monitor_resources() {    FILE *fp;    char buffer[128];
    & Y2 {/ O; l4 F9 ?5 n$ s; ]    // 記錄當(dāng)前時(shí)間    time_t now = time(NULL);    fprintf(log_fp, "Timestamp: %s", ctime(&now));
    9 x# F6 Q$ q) I/ o+ ?    // 記錄 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: %s* S+ K4 O2 f6 T, h/ ~: C3 A
    ", buffer);        pclose(fp);    }2 D7 e8 g: ^( f3 j! d% V+ I
        // 記錄磁盤(pán)使用情況    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);    }
    # N- x# g. ~, \! i: \5 j2 x    fflush(log_fp);  // 確保日志刷新到文件}
    ' p* O5 {! A+ D. D7 H9 m// 定時(shí)清理 /tmp 目錄void cleanup_tmp() {    DIR *dir;    struct dirent *entry;    char file_path[256];
    $ I3 m& e6 d' R* y7 r    dir = opendir(TMP_DIR);    if (dir == NULL) {        syslog(LOG_ERR, "Failed to open /tmp directory");        return;    }
    # P4 Y+ n% U+ Z    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);            }        }    }
    4 y* ~4 y8 Y$ f% [1 t0 [    closedir(dir);}
    ' K! `. `2 V  ]4 C; Uint main() {    daemonize();
    % M* T6 [& T4 C" c4 k) u    // 打開(kāi)日志文件    log_fp = fopen(LOG_FILE, "a");    if (log_fp == NULL) {        syslog(LOG_ERR, "Failed to open log file");        exit(EXIT_FAILURE);    }2 v* ?3 d! y/ p  L/ V5 A( Y+ K% u
        // 捕獲信號(hào)處理    signal(SIGTERM, handle_signal);  // 用于進(jìn)程關(guān)閉    signal(SIGHUP, handle_signal);   // 用于重新加載配置
    ) g8 I: J) K+ J    time_t last_cleanup = time(NULL);. @' L; s: U# H4 E* |7 u5 b' D! u
        // 主循環(huán)    while (keep_running) {        monitor_resources();  // 監(jiān)控系統(tǒng)資源
    " d/ ]- e6 m3 U* w4 f1 X8 k        // 檢查是否需要清理 tmp 目錄        if (difftime(time(NULL), last_cleanup) >= CLEANUP_INTERVAL) {            cleanup_tmp();            last_cleanup = time(NULL);        }
    % t4 L9 j- c  E; Q% H3 H/ E        // 等待 30 秒后繼續(xù)        sleep(MONITOR_INTERVAL);    }
    ' j1 M8 y6 ~8 U* k    // 清理資源并退出    if (log_fp) {        fclose(log_fp);    }    closelog();
    0 W0 x) z9 W9 l* B0 g8 q- c    return 0;}
    $ C. |9 P& V& D( t4 k2.4、代碼詳解# c8 I( y% r  l" |5 y# Y% P
    守護(hù)進(jìn)程初始化 (daemonize)
    ( F7 h1 j+ j1 l9 v$ o
  • 將進(jìn)程變?yōu)槭刈o(hù)進(jìn)程,使用了雙 fork() 技術(shù),確保進(jìn)程在后臺(tái)運(yùn)行并與終端脫離關(guān)系。
  • 使用 syslog 系統(tǒng)日志服務(wù)記錄進(jìn)程啟動(dòng)、關(guān)閉等信息。
    8 |" U. R3 J/ J: J' L. w+ }6 x

    9 h* p" t8 M! U1 d4 @- h2 ^信號(hào)處理 (handle_signal)
    " z6 r. _( j; B+ H; ^4 M) G
  • 通過(guò) signal() 函數(shù)捕獲 SIGTERM 和 SIGHUP 信號(hào)。
  • SIGTERM 信號(hào)用于干凈地終止守護(hù)進(jìn)程。
  • SIGHUP 信號(hào)用于重新加載配置文件,這里模擬了重新打開(kāi)日志文件的過(guò)程。3 B) V+ X4 d3 L" \8 N* n
    & P& Z$ B9 ^& g- F9 {$ w
    資源監(jiān)控 (monitor_resources)3 ~% S0 k1 a3 J  G9 C' j
  • 使用 vmstat 命令監(jiān)控 CPU 和內(nèi)存使用情況,df 命令獲取磁盤(pán)使用狀態(tài)。
  • 每次監(jiān)控結(jié)果都記錄到日志文件中。  A* a: H' R. {8 G
    - ^/ s* M7 J* h- h
    定時(shí)清理 (cleanup_tmp)0 q- t6 s+ M2 s4 C4 i# @3 f1 ~. r- N
  • 每隔 10 分鐘清理 /tmp 目錄下的文件。
  • 僅刪除常規(guī)文件,忽略目錄等。8 r1 e: f6 H+ L

    . m# ]) ?; a2 g# O: R+ d主循環(huán)
    % N2 V: [. s' t6 c( y" @' W
  • 守護(hù)進(jìn)程每 30 秒調(diào)用監(jiān)控和清理函數(shù),保持持續(xù)運(yùn)行狀態(tài)。
    ( P, f% A; N# O1 ]* _
    ' r, {5 |5 |2 r" x( V) b( \/ w* `3 z
    3! Q& `" N5 x& [3 Q
    編譯和運(yùn)行守護(hù)進(jìn)程
    * W9 t: _$ j. g+ v6 V: v( M) k將上述代碼保存為 resource_monitor.c,使用以下命令進(jìn)行編譯和運(yùn)行:
    ) ]- s; t+ F; `; A" F; [. N1 T. X, y- j7 u) w9 ]2 {4 h
  • gcc resource_monitor.c -o resource_monitorsudo ./resource_monitor
    7 k  H# o" i' h2 X3 n  e4 b注意,守護(hù)進(jìn)程需要寫(xiě)入 /var/log/resource_monitor.log 文件,因此需要使用 sudo 權(quán)限運(yùn)行。8 G& ]# X! m  T# ?
    4( `4 n0 I' L0 ^* l3 U0 A3 b
    檢查守護(hù)進(jìn)程
    6 \. ~( h" Y4 N& k查看日志文件內(nèi)容:, s( o, j' i* g* N) O0 S
    - ]! v1 B$ n) e0 t8 R
  • cat /var/log/resource_monitor.log
    6 ^3 u7 w' _: z# R" f, `1 |查看守護(hù)進(jìn)程狀態(tài):
    ( p0 M6 L' j+ E% G& n3 W0 W  o8 t  L. `7 `& r, ]
  • ps -ef | grep resource_monitor
    / n7 `' e1 l3 G8 e可以使用 kill 命令根據(jù)守護(hù)進(jìn)程的 PID 將其終止:/ n3 \8 t1 b/ H: o8 w' F

    ; ]) D7 f2 E7 j0 J) `) n. M
  • kill
    9 K) G5 N, ]8 C3 _" u4 q( e; o1 Q6 N 8 ~# d3 H, V5 d0 E! ]; h9 P
    2 q2 ]- o8 t5 m" j5 ]3 }& V
    點(diǎn)擊閱讀原文,更精彩~
  • 發(fā)表回復(fù)

    本版積分規(guī)則


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