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

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

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

嵌入式Linux:進(jìn)程如何處理信號(hào)

[復(fù)制鏈接]

532

主題

532

帖子

3120

積分

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

Rank: 4

積分
3120
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-8-30 12:02:00 | 只看該作者 |只看大圖 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
: u) \5 y! [5 w1 l
點(diǎn)擊上方藍(lán)色字體,關(guān)注我們* ?  o! y' L1 W, A- p% T
在Linux系統(tǒng)中,當(dāng)進(jìn)程接收到信號(hào)后,可以通過設(shè)置信號(hào)處理方式來決定如何響應(yīng)信號(hào)。
  D- }: a/ r: |) J5 ~" a! \3 M4 X4 R
6 \' m  a/ k! s: p通常,信號(hào)的處理方式可以是以下三種之一:
% l* }. e- G  r) |0 b7 `* s/ w2 D
  • 忽略信號(hào):進(jìn)程對(duì)該信號(hào)不做任何處理,直接忽略。
  • 捕獲信號(hào):為該信號(hào)設(shè)置一個(gè)處理函數(shù),當(dāng)信號(hào)到達(dá)時(shí)執(zhí)行該函數(shù)。
  • 執(zhí)行系統(tǒng)默認(rèn)操作:采用系統(tǒng)預(yù)定義的信號(hào)處理方式。
    ; `+ g" k0 \8 A0 O7 s  _5 l

    ; }! k9 i* F4 q7 I本篇文章主要講解進(jìn)程如何處理信號(hào)。Linux 系統(tǒng)提供了兩個(gè)主要的函數(shù) signal() 和 sigaction() 用于設(shè)置信號(hào)的處理方式。. o4 o/ r- M! l! X4 _9 b0 {/ H3 u
    1
    ( k. V; ~. G5 V1 m$ j; g5 n  s7 ?) ?signal()函數(shù)
    1 Y6 O& p7 [7 \. O  i( Csignal()函數(shù)的原型如下:4 V# f% T, ], o6 @7 x+ q) k- K
    8 F* J- X7 X. \- p' S
  • #include  typedef void (*sig_t)(int); sig_t signal(int signum, sig_t handler);
    ; i, e3 `% _$ u; Z& O4 n% }函數(shù)參數(shù)和含義:
    % r! a; p  R7 f- s. Q
  • signum:指定需要進(jìn)行設(shè)置的信號(hào)。你可以使用信號(hào)的名稱(如SIGINT)或者其對(duì)應(yīng)的數(shù)字編號(hào)。不過,建議使用信號(hào)名稱,因?yàn)檫@樣可讀性更強(qiáng)。
  • handler:這是一個(gè)sig_t類型的函數(shù)指針,用于指向信號(hào)的處理函數(shù)。
  • handler可以設(shè)置為以下幾種:1 J: j, `) I# m  f
  • 用戶自定義函數(shù):這是一個(gè)處理函數(shù),在接收到信號(hào)時(shí)會(huì)自動(dòng)調(diào)用這個(gè)函數(shù)。該函數(shù)的參數(shù)是一個(gè)int類型的值,表示觸發(fā)該函數(shù)的信號(hào)編號(hào)。通過這個(gè)參數(shù),你可以在一個(gè)函數(shù)中處理多個(gè)信號(hào)。
  • SIG_IGN:表示忽略該信號(hào),進(jìn)程在接收到該信號(hào)時(shí)不會(huì)進(jìn)行任何處理。
  • SIG_DFL:表示采用系統(tǒng)的默認(rèn)處理方式,系統(tǒng)會(huì)對(duì)信號(hào)進(jìn)行其預(yù)定義的操作。3 U6 L5 r7 Y* Y+ v3 Y' H

    / M8 T1 q- b% d& o2 O返回值:signal()函數(shù)的返回值是一個(gè)sig_t類型的函數(shù)指針。成功調(diào)用時(shí),返回指向之前信號(hào)處理函數(shù)的指針,這意味著你可以保存這個(gè)指針,以便在將來恢復(fù)原來的信號(hào)處理方式。如果調(diào)用失敗,則返回SIG_ERR,并設(shè)置errno以指示錯(cuò)誤原因。" Q; D9 ?, t* w  K$ r  |
    0 [: `+ W( W1 W' s
    以下是一個(gè)簡單的示例代碼,展示如何使用signal()函數(shù)來捕獲SIGINT信號(hào),并執(zhí)行自定義的信號(hào)處理函數(shù):
    ' F" f1 b3 C3 a/ v! V7 z5 M( ?; m. Z: z* s4 x  |
  • #include #include #include // 自定義信號(hào)處理函數(shù)void handle_signal(int signal) {    printf("Caught signal %d  f. m6 x! J# P
    ", signal);} int main() {    // 將 SIGINT 信號(hào)處理方式設(shè)置為自定義的 handle_signal 函數(shù)    signal(SIGINT, handle_signal);     // 無限循環(huán),等待信號(hào)    while(1) {        printf("Running..., m4 i' F, L" s( l( c( Y
    ");        sleep(1);    }     return 0;}! k4 E" W5 F5 L) w2 c
    在上述代碼中,當(dāng)用戶按下CTRL+C(觸發(fā)SIGINT信號(hào))時(shí),自定義的handle_signal()函數(shù)會(huì)被調(diào)用,并輸出捕獲的信號(hào)編號(hào)。程序會(huì)繼續(xù)運(yùn)行,而不會(huì)終止。如果要忽略SIGINT信號(hào),可以將signal(SIGINT, handle_signal);改為signal(SIGINT, SIG_IGN);。& m) G4 d$ d. A$ W% P  E5 I
    22 }. l" v7 B/ S7 b' n" {0 s
    sigaction() 函數(shù)+ @6 ^% R1 |8 _0 U4 v, Z- A
    sigaction() 函數(shù)是 Linux 系統(tǒng)中用于設(shè)置信號(hào)處理方式的一個(gè)更強(qiáng)大且靈活的系統(tǒng)調(diào)用。與 signal() 函數(shù)相比,sigaction() 提供了更詳細(xì)的控制和更高的移植性,因此更推薦在實(shí)際開發(fā)中使用它。
    " E% ]* s9 I9 B- @, o
    ; K+ r+ s6 }! P/ p" ?  o: u  c: zsigaction() 函數(shù)原型如下:
    5 C1 X) _8 s6 y( Y! w- s, g) H/ w6 O% k0 A8 N7 n- Q3 u
  • #include  int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
    2 _3 ^" R' Q# ~! E* F6 T函數(shù)參數(shù):
    $ ^. y- B1 g0 s0 V# e- c* X( i
  • signum:指定要設(shè)置處理方式的信號(hào)編號(hào)。可以為除 SIGKILL 和 SIGSTOP 以外的任何信號(hào)。
  • act:指向 struct sigaction 結(jié)構(gòu)體的指針,用于指定信號(hào)的新的處理方式。如果 act 為 NULL,則不改變信號(hào)的處理方式。
  • oldact:指向 struct sigaction 結(jié)構(gòu)體的指針,用于存儲(chǔ)信號(hào)先前的處理方式。如果不需要獲取原來的處理方式,可將其設(shè)置為 NULL。2 X" c& k' Q. r$ _
    " l9 ^/ h0 W4 H# |! F8 Z2 ?+ k
    返回值:成功返回 0;失敗返回 -1,并設(shè)置 errno。
    # I: ^7 ?9 [% o. M( k( w! \- @: u
    ! X( P$ |! Y$ {* |$ u: Z7 ]* Nstruct sigaction 結(jié)構(gòu)體用于描述信號(hào)的處理方式,定義如下:
    8 G7 {" P: y: y/ T* i
    8 [1 }5 A! V. l, A1 B7 L/ S0 s  |8 h
  • struct sigaction {    void (*sa_handler)(int);    void (*sa_sigaction)(int, siginfo_t *, void *);    sigset_t sa_mask;    int sa_flags;    void (*sa_restorer)(void);};! V% r* z3 H8 {$ P- F. m
    成員變量如下:0 a2 U3 h& k5 G/ ]' r' j. h
  • sa_handler:信號(hào)處理函數(shù)指針,與 signal() 函數(shù)中的 handler 參數(shù)相同?稍O(shè)置為自定義函數(shù)、SIG_IGN(忽略信號(hào))或 SIG_DFL(系統(tǒng)默認(rèn)處理)。
  • sa_sigaction:另一個(gè)信號(hào)處理函數(shù)指針,用于處理帶有更多信息的信號(hào)。與 sa_handler 互斥,通常使用 sa_handler。選擇使用 sa_sigaction 需設(shè)置 SA_SIGINFO 標(biāo)志。
  • sa_mask:定義在執(zhí)行信號(hào)處理函數(shù)期間要阻塞的信號(hào)集合,以避免信號(hào)之間的競爭條件。
  • sa_flags:標(biāo)志位,用于控制信號(hào)的處理行為。常用標(biāo)志包括:- V9 R4 W* N: a& ?
  • SA_NOCLDSTOP:阻止當(dāng)子進(jìn)程停止或恢復(fù)時(shí)發(fā)送 SIGCHLD 信號(hào)。
  • SA_NOCLDWAIT:子進(jìn)程終止時(shí)不變?yōu)榻┦M(jìn)程。
  • SA_NODEFER:不阻塞自身的信號(hào)。
  • SA_RESETHAND:執(zhí)行完信號(hào)處理后將信號(hào)恢復(fù)為默認(rèn)處理方式。
  • SA_RESTART:被信號(hào)中斷的系統(tǒng)調(diào)用在信號(hào)處理完成后重新發(fā)起。
  • SA_SIGINFO:使用 sa_sigaction 代替 sa_handler。
    ; X; G8 \0 |3 W7 `5 w# ?
  • sa_restorer:已過時(shí),通常不使用。$ A2 E. u& y  w" W
    & o' H  k8 p7 Q/ t! e# D
    siginfo_t 結(jié)構(gòu)體用于在 sa_sigaction 處理信號(hào)時(shí)傳遞更多的上下文信息,結(jié)構(gòu)體定義如下:
    9 I' u5 U  b1 r4 s+ |7 y# p9 F# s3 H9 D4 O
  • typedef struct siginfo {    int si_signo;       /* Signal number */    int si_errno;       /* An errno value */    int si_code;        /* Signal code */    pid_t si_pid;       /* Sending process ID */    uid_t si_uid;       /* Real user ID of sending process */    void *si_addr;      /* Memory location which caused fault */    int si_status;      /* Exit value or signal */    int si_band;        /* Band event */    // ... 其他成員} siginfo_t;" w* F/ R% O6 f
    下面是一個(gè)使用 sigaction() 捕獲 SIGINT 信號(hào)的示例代碼:
    % c4 c6 v4 T! }) g, j5 |
    ( Y% v7 p7 I. p1 C
  • #include #include #include void handle_signal(int signal, siginfo_t *info, void *ucontext) {    printf("Caught signal %d
    2 `5 H3 E' ^. ~  w", signal);    printf("Signal sent by process %d2 N2 ~- A0 ?+ ]" E4 h
    ", info->si_pid);} int main() {    struct sigaction act;    act.sa_sigaction = handle_signal;    act.sa_flags = SA_SIGINFO;  // 使用 sa_sigaction 而不是 sa_handler    sigemptyset(&act.sa_mask);        sigaction(SIGINT, &act, NULL);     // 無限循環(huán),等待信號(hào)    while(1) {        printf("Running...
    1 [" g/ F, y3 u3 m7 h/ h");        sleep(1);    }     return 0;}
    1 A7 B. _" A+ v3 r在這段代碼中,sigaction() 用來設(shè)置 SIGINT 信號(hào)的處理方式。當(dāng)用戶按下 CTRL+C 發(fā)送 SIGINT 信號(hào)時(shí),程序會(huì)調(diào)用 handle_signal() 函數(shù),該函數(shù)可以通過 siginfo_t 結(jié)構(gòu)體獲取信號(hào)的更多信息,比如發(fā)送信號(hào)的進(jìn)程 ID。
    + R0 a/ c2 d0 ]$ u5 j3
    0 b% q$ n- g  \- T0 ]% f注意事項(xiàng)
    5 k( T3 g  \: {1 X3 p  F9 p當(dāng)一個(gè)應(yīng)用程序剛啟動(dòng)時(shí),或在程序中未調(diào)用 signal() 或 sigaction() 來顯式設(shè)置信號(hào)處理方式時(shí),進(jìn)程對(duì)所有信號(hào)的處理方式通常為系統(tǒng)默認(rèn)操作。這意味著大多數(shù)信號(hào)在未被特殊處理的情況下,都會(huì)執(zhí)行默認(rèn)的處理動(dòng)作。2 f9 s( c/ n8 ~* Y9 ^  [) _$ q* S

    * q) v8 z  x' t- ~2 S* h當(dāng)一個(gè)進(jìn)程使用 fork() 系統(tǒng)調(diào)用創(chuàng)建一個(gè)子進(jìn)程時(shí),子進(jìn)程會(huì)繼承父進(jìn)程的信號(hào)處理方式。由于子進(jìn)程是通過復(fù)制父進(jìn)程的內(nèi)存映像而創(chuàng)建的,所以信號(hào)捕獲函數(shù)的地址在子進(jìn)程中同樣有效。這意味著子進(jìn)程將會(huì)繼承父進(jìn)程的信號(hào)處理函數(shù)和其他相關(guān)的信號(hào)處理狀態(tài)。
    , P1 ~  o* E7 T9 V& f, Q
      U; k* H2 ]4 Q* I: h1 i, O1 r這種繼承機(jī)制確保了子進(jìn)程在初始狀態(tài)下能夠正確處理信號(hào),避免因?yàn)槲炊x的信號(hào)處理而導(dǎo)致不可預(yù)測的行為。如果需要,子進(jìn)程可以在運(yùn)行過程中修改其信號(hào)處理方式,從而實(shí)現(xiàn)特定的行為需求。
    9 ~/ p# k# C9 v8 H' L6 A0 q; W& g9 h$ W- M) R
    在設(shè)計(jì)信號(hào)處理函數(shù)時(shí),通常建議保持其簡單性。這與設(shè)計(jì)中斷處理函數(shù)的原則相似:處理函數(shù)應(yīng)盡可能簡短和高效,避免執(zhí)行大量耗費(fèi) CPU 時(shí)間的操作。
      X' i9 z" V9 s: i主要原因如下:
    3 w# z: `6 m/ l( @8 f* r
    8 j  M- ~( H/ r# ~! D- J# t
  • 減少信號(hào)競爭條件:信號(hào)競爭條件(Race Condition)指的是在多線程或多進(jìn)程環(huán)境中,不同信號(hào)可能在不合適的時(shí)間內(nèi)打斷正在處理的代碼,導(dǎo)致不可預(yù)測的結(jié)果。如果信號(hào)處理函數(shù)復(fù)雜且耗時(shí)較長,進(jìn)程在執(zhí)行處理函數(shù)時(shí),可能會(huì)接收到相同或其他信號(hào),增加競爭條件發(fā)生的風(fēng)險(xiǎn)。
  • 保證系統(tǒng)響應(yīng)性:信號(hào)處理函數(shù)應(yīng)快速完成,以確保系統(tǒng)能夠及時(shí)響應(yīng)其他事件或信號(hào)。如果處理函數(shù)占用了大量的 CPU 時(shí)間,系統(tǒng)響應(yīng)速度可能會(huì)受到影響,尤其是在實(shí)時(shí)性要求較高的系統(tǒng)中。
  • 減少對(duì)系統(tǒng)狀態(tài)的影響:復(fù)雜的信號(hào)處理函數(shù)可能會(huì)改變進(jìn)程的全局狀態(tài)(如修改全局變量),這可能會(huì)導(dǎo)致進(jìn)程在信號(hào)處理完成后進(jìn)入不一致的狀態(tài)。因此,簡單的處理函數(shù)可以減少這些副作用。
    : s. H* _( ]' z, R) {" T% c5 P

    9 ?7 E! l* a1 \- x( U5 p6 ^9 V最佳實(shí)踐:+ Q6 v0 N( Y0 n
  • 在信號(hào)處理函數(shù)中,只執(zhí)行必要的操作,如設(shè)置一個(gè)標(biāo)志或記錄一個(gè)簡單的狀態(tài)。
  • 如果需要執(zhí)行復(fù)雜的邏輯,可以在信號(hào)處理函數(shù)中設(shè)置一個(gè)標(biāo)志,然后在主程序的主循環(huán)中檢查該標(biāo)志,并執(zhí)行相應(yīng)的復(fù)雜邏輯。( E6 R8 s' u+ G$ {
    這種方式可以有效分離信號(hào)處理與復(fù)雜邏輯,降低風(fēng)險(xiǎn)。  g& h7 \  ~! b
    , k; @3 u# ?1 c# u
    通過保持信號(hào)處理函數(shù)的簡單性,你可以有效提高程序的穩(wěn)定性和可靠性,減少潛在的問題和復(fù)雜的調(diào)試過程。
    7 j" H" [6 s  F1 p! i" [9 m5 M: o; |1 t0 V0 S
    $ G( J2 v$ {, n3 x& J9 u

    # U3 u; E5 P' V3 n) p點(diǎn)擊閱讀原文,更精彩~
  • 發(fā)表回復(fù)

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

    本版積分規(guī)則

    關(guān)閉

    站長推薦上一條 /1 下一條


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