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

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

搜索
查看: 72|回復(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覽 |閱讀模式

) _1 e1 J: n& i) _5 i" J" w點(diǎn)擊上方藍(lán)色字體,關(guān)注我們
* h8 [0 Z- P- @0 p
  R* w9 E, a  p8 {6 D* l在命令輸出中,如果 TTY 一欄顯示為問(wèn)號(hào)(?),這表示該進(jìn)程沒(méi)有控制終端,通常意味著它是一個(gè)守護(hù)進(jìn)程。同時(shí),COMMAND 一欄中用中括號(hào)([])括起來(lái)的進(jìn)程表示內(nèi)核線(xiàn)程。# d" ?! l& `! H9 W( o; Z0 M' z

2 `% t! [2 @$ Z4 W這些線(xiàn)程是在內(nèi)核空間中創(chuàng)建的,沒(méi)有對(duì)應(yīng)的用戶(hù)空間代碼,因此不具備程序文件名和命令行信息,通常以字母 k 開(kāi)頭,表示它們是內(nèi)核線(xiàn)程(Kernel)。- {; J* N9 s. d* ]
1
7 R* M2 M9 P$ L3 Y) |% d$ s6 v編寫(xiě)守護(hù)進(jìn)程的步驟8 A, I+ G) v* G& C# n0 W* n
編寫(xiě)守護(hù)進(jìn)程通常包括以下幾個(gè)關(guān)鍵步驟,以確保其能夠在后臺(tái)獨(dú)立運(yùn)行,并完成預(yù)定的任務(wù)。
+ q& X, k- X5 e- m6 @4 I2 A5 x' i8 w. Q% ]& }
1、創(chuàng)建子進(jìn)程并終止父進(jìn)程
' n* X& }' U# ^( h使用 fork() 創(chuàng)建子進(jìn)程后,父進(jìn)程應(yīng)調(diào)用 exit() 終止自身。這一過(guò)程實(shí)現(xiàn)了以下幾點(diǎn):, e: r! i, R) l2 u+ t
  • 如果守護(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)備條件。( q* w. O9 }. B% l( c

    7 H7 E' F7 N& ~. W4 P* c2、子進(jìn)程調(diào)用 setsid() 創(chuàng)建會(huì)話(huà)7 o& N# N, O1 x7 i9 J0 g( ~! c
    在子進(jìn)程中調(diào)用 setsid() 是關(guān)鍵步驟。這將:
    # ?* m/ M  M+ u- L- X8 K" n6 Z7 m
  • 創(chuàng)建一個(gè)新的會(huì)話(huà),子進(jìn)程成為新會(huì)話(huà)的首領(lǐng)。
  • 創(chuàng)建新的進(jìn)程組,子進(jìn)程成為組長(zhǎng)。
  • 擺脫原有會(huì)話(huà)、進(jìn)程組和控制終端的控制,實(shí)現(xiàn)完全獨(dú)立。
    4 \: Z  o- Y) i, o3 s: v盡管子進(jìn)程在 fork() 時(shí)繼承了父進(jìn)程的控制權(quán),但 setsid() 能確保其完全脫離。+ }6 z* D0 l* S6 ~
    6 r! [: {* x5 G, L) M/ g9 m) h
    3、更改工作目錄為根目錄0 e; d) q% I& K0 X) x& z
    子進(jìn)程會(huì)繼承父進(jìn)程的當(dāng)前工作目錄,而該目錄可能會(huì)導(dǎo)致文件系統(tǒng)無(wú)法卸載。通常,守護(hù)進(jìn)程會(huì)將工作目錄更改為根目錄(/),以避免這種問(wèn)題。也可以根據(jù)需要選擇其他目錄。
    $ d4 N% ?% W$ j3 J6 I  g
    / V( Y1 ?, a/ e, u4、重設(shè)文件權(quán)限掩碼(umask)' p$ j" J" r9 `, r) m4 d
    文件權(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)。
    # q$ }& r; o( W2 i  |4 B
    , ?6 \' f- F8 e5、關(guān)閉不再需要的文件描述符
    ) g( r6 r2 q$ |7 C子進(jìn)程會(huì)繼承父進(jìn)程打開(kāi)的所有文件描述符,這可能導(dǎo)致不必要的資源消耗。應(yīng)關(guān)閉不再需要的文件描述符,以確保守護(hù)進(jìn)程不再持有任何繼承自父進(jìn)程的描述符,從而減少資源浪費(fèi)。5 |# D4 Z3 V+ \9 C
    : T/ O4 K; h, p  x
    6、將文件描述符 0、1、2 定位到 /dev/null1 H# |# G4 @7 w" ~4 Q6 ]
    守護(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ì)試圖從交互式用戶(hù)那里接收輸入。( f$ G" `1 `; L! D* m

    ! f0 B9 L2 F0 c7、其他處理:忽略 SIGCHLD 信號(hào)
    ' L4 [5 H: s, Z3 r處理 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ā)性能。
    ; r& d* k, P* a, h2
    * y* ]7 P/ ^' Y7 r守護(hù)進(jìn)程的使用和案例設(shè)計(jì)
    " |% T. y) y3 p* }: R$ O0 T為了深入理解如何創(chuàng)建和使用守護(hù)進(jìn)程,我們將創(chuàng)建一個(gè)多功能的守護(hù)進(jìn)程,具備以下功能:1 @0 J/ v9 N! b5 P8 S9 b: J$ B
  • 資源監(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 ~0 O" y0 M$ r0 o

    3 I) I3 \/ O  M* Z2.1、案例功能分析
    / d5 g+ T" ?) V/ p系統(tǒng)資源監(jiān)控
    ( R( v+ J9 r$ Y( j, d6 ^
  • 使用系統(tǒng)命令 stat 和 vmstat 來(lái)獲取 CPU 和內(nèi)存信息。
  • 使用 df 命令獲取磁盤(pán)使用情況。
  • 每次獲取的信息都寫(xiě)入 /var/log/resource_monitor.log,便于運(yùn)維人員檢查系統(tǒng)的健康狀態(tài)。
      d# c2 _. ~) h6 ?/ ]( Y

    1 B  T: A# k& M' i. r定時(shí)清理任務(wù)
    4 k/ U+ g' e8 Z; d
  • 每隔 10 分鐘調(diào)用一個(gè)函數(shù)清理 /tmp 目錄下的文件。
  • 使用系統(tǒng)函數(shù) unlink() 刪除文件。" @! w8 ?1 y$ X* d; m
    + F# q6 z  @8 V% T( e! ^
    信號(hào)處理3 e  m7 n  |6 N( m8 g8 e. Y  \1 r
  • 捕獲 SIGTERM 信號(hào),干凈地終止守護(hù)進(jìn)程并進(jìn)行資源釋放。
  • 捕獲 SIGHUP 信號(hào),重新加載配置文件(如改變?nèi)罩疚募穆窂剑?font class="jammer">6 v8 s7 _) X6 h1 I2 C$ @- J% x5 l

    3 P" h" G! {$ y+ |& M$ Y7 H6 n# |9 h0 l2.2、守護(hù)進(jìn)程代碼結(jié)構(gòu); a3 v' T& K- ]0 M8 Q
  • 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í),重新加載配置文件。
    " I! F0 I- F. T$ ^6 s" C
    6 a+ }5 m5 T( e% Q: W7 C1 _
    2.3、代碼實(shí)現(xiàn)
    # d/ a6 P4 M# A' H. T( S
  • #define LOG_FILE "/var/log/resource_monitor.log"#define CONFIG_FILE "/etc/daemon_config.conf"#define TMP_DIR "/tmp"
    - B  B1 d6 k8 d8 C// 定義輪詢(xún)時(shí)間#define MONITOR_INTERVAL 30  // 資源監(jiān)控間隔 30 秒#define CLEANUP_INTERVAL 600 // 清理間隔 10 分鐘8 i, j  b4 A: Y% `- \
    int keep_running = 1;FILE *log_fp = NULL;, W/ y0 P$ S; N6 E1 E! a
    // 守護(hù)進(jìn)程初始化函數(shù)void daemonize() {    pid_t pid;
    + u6 D8 n, `6 {) y; D* y: z* Y    // 1. 創(chuàng)建子進(jìn)程并終止父進(jìn)程    pid = fork();    if (pid 0) exit(EXIT_FAILURE);    if (pid > 0) exit(EXIT_SUCCESS);  // 父進(jìn)程退出0 z% c( R- n% V- G- k# [9 f, X
        // 2. 創(chuàng)建新的會(huì)話(huà)    if (setsid() 0) exit(EXIT_FAILURE);
    * X2 h% O1 ~' F3 w* M    // 3. 忽略 SIGCHLD 信號(hào)    signal(SIGCHLD, SIG_IGN);$ k: g* _- h2 M0 |, `4 n. Y+ {
        // 4. 再次 fork,防止守護(hù)進(jìn)程重新獲得終端    pid = fork();    if (pid 0) exit(EXIT_FAILURE);    if (pid > 0) exit(EXIT_SUCCESS);
    3 r* K; d$ m% U9 A: s    // 5. 更改工作目錄到根目錄    chdir("/");
    + P. `! x. s  ?, o    // 6. 重設(shè)文件權(quán)限掩碼    umask(0);( y' \6 f: R/ f: N9 x2 D: ^7 b
        // 7. 關(guān)閉不再需要的文件描述符    close(STDIN_FILENO);    close(STDOUT_FILENO);    close(STDERR_FILENO);
    : e+ B6 O9 f' j. t8 T# I7 d    // 8. 重定向標(biāo)準(zhǔn)輸入、輸出、錯(cuò)誤到 /dev/null    open("/dev/null", O_RDONLY);    open("/dev/null", O_WRONLY);    open("/dev/null", O_WRONLY);
    5 _1 l  ~$ W* x2 ?9 E: W# Q5 U    // 打開(kāi)系統(tǒng)日志    openlog("resource_daemon", LOG_PID, LOG_DAEMON);}
    - o% V) u- P2 z3 v5 ]" z* c// 捕獲信號(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;    }}
    # T; N* r# m. P. [' ^  e7 H% I5 o// 資源監(jiān)控功能void monitor_resources() {    FILE *fp;    char buffer[128];
    5 B% Q% _$ N/ z  \+ ^7 ]6 _    // 記錄當(dāng)前時(shí)間    time_t now = time(NULL);    fprintf(log_fp, "Timestamp: %s", ctime(&now));
    ' P7 H% X  }2 ~  g    // 記錄 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; f, k" u3 `. \. Y/ @; T& u
    ", buffer);        pclose(fp);    }5 ]# ^1 b7 R/ z: a( K
        // 記錄磁盤(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);    }* g2 o' ^7 R; c
        fflush(log_fp);  // 確保日志刷新到文件}
    + q2 n" Q$ ~, M6 K; O3 f; ?# O// 定時(shí)清理 /tmp 目錄void cleanup_tmp() {    DIR *dir;    struct dirent *entry;    char file_path[256];0 X4 y1 p3 `# E3 @6 Y
        dir = opendir(TMP_DIR);    if (dir == NULL) {        syslog(LOG_ERR, "Failed to open /tmp directory");        return;    }5 q6 o6 e: G* _* d  ~1 l% E6 C6 |, K
        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);            }        }    }, N9 P% J" [- h
        closedir(dir);}
    # S& w2 S& f; _- l* p# O# qint main() {    daemonize();3 v. ]8 i' n, e( f, ^* n
        // 打開(kāi)日志文件    log_fp = fopen(LOG_FILE, "a");    if (log_fp == NULL) {        syslog(LOG_ERR, "Failed to open log file");        exit(EXIT_FAILURE);    }; }7 G+ I3 H' p; U% S* y. k
        // 捕獲信號(hào)處理    signal(SIGTERM, handle_signal);  // 用于進(jìn)程關(guān)閉    signal(SIGHUP, handle_signal);   // 用于重新加載配置
    1 }' h; Q- q# f* l. Q" p" M    time_t last_cleanup = time(NULL);
    # z1 `7 v1 m8 {3 s    // 主循環(huán)    while (keep_running) {        monitor_resources();  // 監(jiān)控系統(tǒng)資源& h/ q3 |- E4 l) a
            // 檢查是否需要清理 tmp 目錄        if (difftime(time(NULL), last_cleanup) >= CLEANUP_INTERVAL) {            cleanup_tmp();            last_cleanup = time(NULL);        }
    ) k& n  }# k+ D) o- H2 p' N, V. k- m        // 等待 30 秒后繼續(xù)        sleep(MONITOR_INTERVAL);    }
    8 N: I! A5 V* x/ Z3 N$ Z    // 清理資源并退出    if (log_fp) {        fclose(log_fp);    }    closelog();
    * T8 s4 E1 T- U0 F1 j7 e    return 0;}
    5 j  r: z3 G" t8 I2.4、代碼詳解
    % W$ q0 l% i6 @# k0 k) ^& t- p守護(hù)進(jìn)程初始化 (daemonize)
    + i4 e4 Z: \3 K* j9 B1 s
  • 將進(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)閉等信息。
    % K6 l6 `( [2 B8 P; u
    + ^- m  n- \% w, x( o7 q
    信號(hào)處理 (handle_signal)8 Z) Q& y. K2 ?7 ~
  • 通過(guò) signal() 函數(shù)捕獲 SIGTERM 和 SIGHUP 信號(hào)。
  • SIGTERM 信號(hào)用于干凈地終止守護(hù)進(jìn)程。
  • SIGHUP 信號(hào)用于重新加載配置文件,這里模擬了重新打開(kāi)日志文件的過(guò)程。# c3 r/ }$ Y5 e7 n
    7 g4 H! u, H2 Z2 \) ]0 u
    資源監(jiān)控 (monitor_resources)
    7 O7 p# k( e( B2 A8 [+ [+ O
  • 使用 vmstat 命令監(jiān)控 CPU 和內(nèi)存使用情況,df 命令獲取磁盤(pán)使用狀態(tài)。
  • 每次監(jiān)控結(jié)果都記錄到日志文件中。
    ) P, J& Q! l) C; M
    $ ^2 ^5 j- E3 y1 W
    定時(shí)清理 (cleanup_tmp)7 l1 l4 i  O$ s: Y: F5 E3 c$ r
  • 每隔 10 分鐘清理 /tmp 目錄下的文件。
  • 僅刪除常規(guī)文件,忽略目錄等。
    , }3 ?+ K2 t9 _. f7 J$ K

    0 v4 Y6 o9 @3 T' d7 {0 j9 w主循環(huán)
    ) L% A* q% w4 i
  • 守護(hù)進(jìn)程每 30 秒調(diào)用監(jiān)控和清理函數(shù),保持持續(xù)運(yùn)行狀態(tài)。% d7 v% _- k' P

    ' A+ e9 R$ d) k# [$ a1 J5 [; F: f3
    , Z& s! b3 h0 ?: K# p編譯和運(yùn)行守護(hù)進(jìn)程
    , \1 B) u; K4 {" w$ z" J將上述代碼保存為 resource_monitor.c,使用以下命令進(jìn)行編譯和運(yùn)行:) H, l! b+ v. v
    " V5 p- u; |! U, o- f# X6 c
  • gcc resource_monitor.c -o resource_monitorsudo ./resource_monitor7 }: D1 |4 P' E& p: |
    注意,守護(hù)進(jìn)程需要寫(xiě)入 /var/log/resource_monitor.log 文件,因此需要使用 sudo 權(quán)限運(yùn)行。
    , T3 y9 x7 ^4 \4
    ) [3 l0 d9 s% F$ j檢查守護(hù)進(jìn)程
    + S2 x( G6 r! G* m4 M8 b2 s5 I& S查看日志文件內(nèi)容:" {; P3 K9 n2 _* S

    ' S! v' w$ X% R! c
  • cat /var/log/resource_monitor.log
    / ]1 J/ S' `7 R5 @8 g查看守護(hù)進(jìn)程狀態(tài):
    + S5 c$ G' x2 `" J( N2 p$ E3 ]) ~9 F# y: `- Q$ z% }! i
  • ps -ef | grep resource_monitor
    . c5 p4 @. K' r, M# C6 z' A; y可以使用 kill 命令根據(jù)守護(hù)進(jìn)程的 PID 將其終止:
    / S- ]: _1 C' T! {7 P! B
    4 E5 T6 m" ]' x6 p; z
  • kill
    5 L  [) U# I6 s 7 O% M3 K4 [: }4 u8 S- n* L

    5 ~9 l7 I2 ?- x點(diǎn)擊閱讀原文,更精彩~
  • 發(fā)表回復(fù)

    本版積分規(guī)則


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