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

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

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

嵌入式Linux:進程如何處理信號

[復(fù)制鏈接]

532

主題

532

帖子

3120

積分

四級會員

Rank: 4

積分
3120
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2024-8-30 12:02:00 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式

/ r4 P% C1 I" j$ ~& K點擊上方藍色字體,關(guān)注我們
1 q% x: p1 \+ s4 ~# \9 |在Linux系統(tǒng)中,當進程接收到信號后,可以通過設(shè)置信號處理方式來決定如何響應(yīng)信號。8 a* T5 [+ U+ Z
9 s2 |1 i! h" B; ]2 Q. D$ v0 E
通常,信號的處理方式可以是以下三種之一:4 }8 ]) @1 g/ A5 J( u; e+ K. W/ q
  • 忽略信號:進程對該信號不做任何處理,直接忽略。
  • 捕獲信號:為該信號設(shè)置一個處理函數(shù),當信號到達時執(zhí)行該函數(shù)。
  • 執(zhí)行系統(tǒng)默認操作:采用系統(tǒng)預(yù)定義的信號處理方式。
    $ D  S8 O9 m0 c" B' s

    $ O' y- j' {3 d$ e3 G: V本篇文章主要講解進程如何處理信號。Linux 系統(tǒng)提供了兩個主要的函數(shù) signal() 和 sigaction() 用于設(shè)置信號的處理方式。: f# _- T' B: {3 I* v- o
    1
    4 Y; n  z: W, V1 x/ Esignal()函數(shù)# ]+ g9 m6 s: o/ M0 o! d  e8 J
    signal()函數(shù)的原型如下:
    ) F/ V8 o: V+ h, A' X" P! M
    ' P/ p0 C" t2 P* q: m3 _* {
  • #include  typedef void (*sig_t)(int); sig_t signal(int signum, sig_t handler);  O' }/ {: J. t0 `/ E( o
    函數(shù)參數(shù)和含義:
    1 z$ b$ s. q1 j7 S' D# M- b1 }) {" a6 f
  • signum:指定需要進行設(shè)置的信號。你可以使用信號的名稱(如SIGINT)或者其對應(yīng)的數(shù)字編號。不過,建議使用信號名稱,因為這樣可讀性更強。
  • handler:這是一個sig_t類型的函數(shù)指針,用于指向信號的處理函數(shù)。
  • handler可以設(shè)置為以下幾種:
    / c  g, L  v( b, A! q
  • 用戶自定義函數(shù):這是一個處理函數(shù),在接收到信號時會自動調(diào)用這個函數(shù)。該函數(shù)的參數(shù)是一個int類型的值,表示觸發(fā)該函數(shù)的信號編號。通過這個參數(shù),你可以在一個函數(shù)中處理多個信號。
  • SIG_IGN:表示忽略該信號,進程在接收到該信號時不會進行任何處理。
  • SIG_DFL:表示采用系統(tǒng)的默認處理方式,系統(tǒng)會對信號進行其預(yù)定義的操作。4 J$ [( n' a5 c: x+ O- K, o; h
    - D( F' o4 r5 T6 e) l& t
    返回值:signal()函數(shù)的返回值是一個sig_t類型的函數(shù)指針。成功調(diào)用時,返回指向之前信號處理函數(shù)的指針,這意味著你可以保存這個指針,以便在將來恢復(fù)原來的信號處理方式。如果調(diào)用失敗,則返回SIG_ERR,并設(shè)置errno以指示錯誤原因。
    3 _( {' G0 N& N! r  A; P2 |; |, t  P" o  s3 O. N7 G: I
    以下是一個簡單的示例代碼,展示如何使用signal()函數(shù)來捕獲SIGINT信號,并執(zhí)行自定義的信號處理函數(shù):, f1 W2 R+ o9 S4 x
    ( S& ]9 M3 j0 W# V+ d- D
  • #include #include #include // 自定義信號處理函數(shù)void handle_signal(int signal) {    printf("Caught signal %d* {+ [7 g2 G' V2 H
    ", signal);} int main() {    // 將 SIGINT 信號處理方式設(shè)置為自定義的 handle_signal 函數(shù)    signal(SIGINT, handle_signal);     // 無限循環(huán),等待信號    while(1) {        printf("Running...
    - S, d6 D; ~" ~& J! r");        sleep(1);    }     return 0;}1 B! F8 r, g5 E) b; T; i
    在上述代碼中,當用戶按下CTRL+C(觸發(fā)SIGINT信號)時,自定義的handle_signal()函數(shù)會被調(diào)用,并輸出捕獲的信號編號。程序會繼續(xù)運行,而不會終止。如果要忽略SIGINT信號,可以將signal(SIGINT, handle_signal);改為signal(SIGINT, SIG_IGN);。; G1 Z6 b1 L. o1 E* Y# _* I" Y
    2( J# O" m  L+ e
    sigaction() 函數(shù)
    3 U; t: H& W6 m/ T7 Y+ U5 o) Esigaction() 函數(shù)是 Linux 系統(tǒng)中用于設(shè)置信號處理方式的一個更強大且靈活的系統(tǒng)調(diào)用。與 signal() 函數(shù)相比,sigaction() 提供了更詳細的控制和更高的移植性,因此更推薦在實際開發(fā)中使用它。
    * J. }% w# L/ }
    4 a2 P: O  k# j6 [( Y1 Dsigaction() 函數(shù)原型如下:
    $ A5 ^) ~7 C, _: E/ Q$ `. @( ]2 A9 b( L, {. _
  • #include  int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);' c, Z1 m  K8 q7 K; m* `
    函數(shù)參數(shù):
    , o4 E3 c- q' C! M8 A
  • signum:指定要設(shè)置處理方式的信號編號?梢詾槌 SIGKILL 和 SIGSTOP 以外的任何信號。
  • act:指向 struct sigaction 結(jié)構(gòu)體的指針,用于指定信號的新的處理方式。如果 act 為 NULL,則不改變信號的處理方式。
  • oldact:指向 struct sigaction 結(jié)構(gòu)體的指針,用于存儲信號先前的處理方式。如果不需要獲取原來的處理方式,可將其設(shè)置為 NULL。  G; C! ~: w& X. s

    - Z. i" ]1 o& |" |8 [+ Z* V; ^2 p7 {返回值:成功返回 0;失敗返回 -1,并設(shè)置 errno。
    ( F/ a& d0 l% ^' n; m) B. ]! s% ^: x; \/ r. I
    struct sigaction 結(jié)構(gòu)體用于描述信號的處理方式,定義如下:* o: `% E) i: N* \: g3 Y

    6 E9 H2 v7 W* V5 L6 u$ O5 f
  • struct sigaction {    void (*sa_handler)(int);    void (*sa_sigaction)(int, siginfo_t *, void *);    sigset_t sa_mask;    int sa_flags;    void (*sa_restorer)(void);};
    - C$ V1 X* S( M8 o成員變量如下:
    . U7 Z& p, y" |1 N) t7 V( F! ?1 W
  • sa_handler:信號處理函數(shù)指針,與 signal() 函數(shù)中的 handler 參數(shù)相同?稍O(shè)置為自定義函數(shù)、SIG_IGN(忽略信號)或 SIG_DFL(系統(tǒng)默認處理)。
  • sa_sigaction:另一個信號處理函數(shù)指針,用于處理帶有更多信息的信號。與 sa_handler 互斥,通常使用 sa_handler。選擇使用 sa_sigaction 需設(shè)置 SA_SIGINFO 標志。
  • sa_mask:定義在執(zhí)行信號處理函數(shù)期間要阻塞的信號集合,以避免信號之間的競爭條件。
  • sa_flags:標志位,用于控制信號的處理行為。常用標志包括:
    # }. w" o7 y7 {- n
  • SA_NOCLDSTOP:阻止當子進程停止或恢復(fù)時發(fā)送 SIGCHLD 信號。
  • SA_NOCLDWAIT:子進程終止時不變?yōu)榻┦M程。
  • SA_NODEFER:不阻塞自身的信號。
  • SA_RESETHAND:執(zhí)行完信號處理后將信號恢復(fù)為默認處理方式。
  • SA_RESTART:被信號中斷的系統(tǒng)調(diào)用在信號處理完成后重新發(fā)起。
  • SA_SIGINFO:使用 sa_sigaction 代替 sa_handler。) m' d# j6 R* q5 b% |8 e
  • sa_restorer:已過時,通常不使用。
    , D; D1 v9 K4 u6 w0 \6 j
    . S7 P& ?9 X5 @) X! b- B% i
    siginfo_t 結(jié)構(gòu)體用于在 sa_sigaction 處理信號時傳遞更多的上下文信息,結(jié)構(gòu)體定義如下:
    " s* H* t9 ~; V2 o1 t. W
    4 _1 b9 f  O+ X1 D
  • 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;
    ! g3 r' L! K' [: O" ]. q; K下面是一個使用 sigaction() 捕獲 SIGINT 信號的示例代碼:+ y( A( Y/ h2 {+ ^

    ( h! c2 D$ {- C. {5 }+ ?9 D3 o, T
  • #include #include #include void handle_signal(int signal, siginfo_t *info, void *ucontext) {    printf("Caught signal %d! |; Y- O4 w0 M* T1 X' L% x( Z8 C( G
    ", signal);    printf("Signal sent by process %d
    % q9 B$ Z' s$ C  Y: T$ }4 ]", 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),等待信號    while(1) {        printf("Running...
    / ^  S0 @% }  F' V: q");        sleep(1);    }     return 0;}
      y  c7 E/ E% x在這段代碼中,sigaction() 用來設(shè)置 SIGINT 信號的處理方式。當用戶按下 CTRL+C 發(fā)送 SIGINT 信號時,程序會調(diào)用 handle_signal() 函數(shù),該函數(shù)可以通過 siginfo_t 結(jié)構(gòu)體獲取信號的更多信息,比如發(fā)送信號的進程 ID。# o# O" m* l% V) h( [# r
    3; R  m4 n" n& }' R4 V
    注意事項" ?& K' z- _; S" m' Z5 v
    當一個應(yīng)用程序剛啟動時,或在程序中未調(diào)用 signal() 或 sigaction() 來顯式設(shè)置信號處理方式時,進程對所有信號的處理方式通常為系統(tǒng)默認操作。這意味著大多數(shù)信號在未被特殊處理的情況下,都會執(zhí)行默認的處理動作。
    * @$ e. ]0 Y, i, E" y
    ; `: Q( s! Y7 `3 R% F; @8 J* \, \% \當一個進程使用 fork() 系統(tǒng)調(diào)用創(chuàng)建一個子進程時,子進程會繼承父進程的信號處理方式。由于子進程是通過復(fù)制父進程的內(nèi)存映像而創(chuàng)建的,所以信號捕獲函數(shù)的地址在子進程中同樣有效。這意味著子進程將會繼承父進程的信號處理函數(shù)和其他相關(guān)的信號處理狀態(tài)。
    & J; W( A5 e6 f- E& ?3 _  L$ H
    . E! a1 t) C' Y2 A  L這種繼承機制確保了子進程在初始狀態(tài)下能夠正確處理信號,避免因為未定義的信號處理而導(dǎo)致不可預(yù)測的行為。如果需要,子進程可以在運行過程中修改其信號處理方式,從而實現(xiàn)特定的行為需求。4 H* n- ?9 r/ s/ E

    ' {+ l- R, e2 Y8 C" y  A在設(shè)計信號處理函數(shù)時,通常建議保持其簡單性。這與設(shè)計中斷處理函數(shù)的原則相似:處理函數(shù)應(yīng)盡可能簡短和高效,避免執(zhí)行大量耗費 CPU 時間的操作。! n- @* F2 D3 w
    主要原因如下:
    " o$ E& [: m3 A2 ^$ t: j. o! w' J5 b$ ]* l4 v+ f- P% z. F
  • 減少信號競爭條件:信號競爭條件(Race Condition)指的是在多線程或多進程環(huán)境中,不同信號可能在不合適的時間內(nèi)打斷正在處理的代碼,導(dǎo)致不可預(yù)測的結(jié)果。如果信號處理函數(shù)復(fù)雜且耗時較長,進程在執(zhí)行處理函數(shù)時,可能會接收到相同或其他信號,增加競爭條件發(fā)生的風險。
  • 保證系統(tǒng)響應(yīng)性:信號處理函數(shù)應(yīng)快速完成,以確保系統(tǒng)能夠及時響應(yīng)其他事件或信號。如果處理函數(shù)占用了大量的 CPU 時間,系統(tǒng)響應(yīng)速度可能會受到影響,尤其是在實時性要求較高的系統(tǒng)中。
  • 減少對系統(tǒng)狀態(tài)的影響:復(fù)雜的信號處理函數(shù)可能會改變進程的全局狀態(tài)(如修改全局變量),這可能會導(dǎo)致進程在信號處理完成后進入不一致的狀態(tài)。因此,簡單的處理函數(shù)可以減少這些副作用。
    6 _0 i3 I: h* N" r, C, M+ C
      ~0 h, t! l% T; A0 t
    最佳實踐:
    # g4 `. `7 R3 K' Z7 m, u
  • 在信號處理函數(shù)中,只執(zhí)行必要的操作,如設(shè)置一個標志或記錄一個簡單的狀態(tài)。
  • 如果需要執(zhí)行復(fù)雜的邏輯,可以在信號處理函數(shù)中設(shè)置一個標志,然后在主程序的主循環(huán)中檢查該標志,并執(zhí)行相應(yīng)的復(fù)雜邏輯。
    6 o! G8 O& f8 [3 i: v7 s0 o% {7 ~這種方式可以有效分離信號處理與復(fù)雜邏輯,降低風險。
    & s% D* B! V. e* T6 f
    ; f4 _0 i; @+ r- a
    通過保持信號處理函數(shù)的簡單性,你可以有效提高程序的穩(wěn)定性和可靠性,減少潛在的問題和復(fù)雜的調(diào)試過程。, R4 C+ B( l% L! k4 g
    5 ^2 z8 W: p, I( }$ ~# n
    5 Z0 C( z2 k( z

    / g* t. o7 T# X$ b1 }2 K7 l點擊閱讀原文,更精彩~
  • 回復(fù)

    使用道具 舉報

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

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

    本版積分規(guī)則

    關(guān)閉

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


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