|
1enzz0fbgvl64011275405.gif (60.41 KB, 下載次數(shù): 0)
下載附件
保存到相冊
1enzz0fbgvl64011275405.gif
2024-8-31 11:41 上傳
, e/ L+ q" G% H* N) k; v1 O, X點擊上方藍色字體,關(guān)注我們6 u7 t `& [/ J0 @2 s
在Linux系統(tǒng)中,當進程接收到信號后,可以通過設(shè)置信號處理方式來決定如何響應(yīng)信號。
" d$ B) ?4 a" \+ z u3 }% r) u4 A# b u' V W3 {4 u
通常,信號的處理方式可以是以下三種之一:* n; Q! z6 R i% x& B; ?
忽略信號:進程對該信號不做任何處理,直接忽略。捕獲信號:為該信號設(shè)置一個處理函數(shù),當信號到達時執(zhí)行該函數(shù)。執(zhí)行系統(tǒng)默認操作:采用系統(tǒng)預(yù)定義的信號處理方式。6 I# M2 T' ?1 L0 S, _
& n: w% K [5 B. ?5 `本篇文章主要講解進程如何處理信號。Linux 系統(tǒng)提供了兩個主要的函數(shù) signal() 和 sigaction() 用于設(shè)置信號的處理方式。
! V* W" a/ Y9 L* O) G: f1 X. i8 Q1
$ C3 \! @# _! }7 xsignal()函數(shù)/ Y$ `% K( j" o9 N- L' h$ Y8 g
signal()函數(shù)的原型如下:0 [7 X F B. D8 ^! O2 S9 B$ y
, ]: Y& ^; f) ~- \: n( i
#include typedef void (*sig_t)(int); sig_t signal(int signum, sig_t handler);6 h, m$ K! ?# W3 k& O2 F
函數(shù)參數(shù)和含義:0 n+ k, W" G! B% k; L! |
signum:指定需要進行設(shè)置的信號。你可以使用信號的名稱(如SIGINT)或者其對應(yīng)的數(shù)字編號。不過,建議使用信號名稱,因為這樣可讀性更強。handler:這是一個sig_t類型的函數(shù)指針,用于指向信號的處理函數(shù)。handler可以設(shè)置為以下幾種:
! |7 ^$ [: h' T' [用戶自定義函數(shù):這是一個處理函數(shù),在接收到信號時會自動調(diào)用這個函數(shù)。該函數(shù)的參數(shù)是一個int類型的值,表示觸發(fā)該函數(shù)的信號編號。通過這個參數(shù),你可以在一個函數(shù)中處理多個信號。SIG_IGN:表示忽略該信號,進程在接收到該信號時不會進行任何處理。SIG_DFL:表示采用系統(tǒng)的默認處理方式,系統(tǒng)會對信號進行其預(yù)定義的操作。3 Y0 J8 @! D9 }
3 v$ p+ {8 X: e$ e7 J; ]
返回值:signal()函數(shù)的返回值是一個sig_t類型的函數(shù)指針。成功調(diào)用時,返回指向之前信號處理函數(shù)的指針,這意味著你可以保存這個指針,以便在將來恢復(fù)原來的信號處理方式。如果調(diào)用失敗,則返回SIG_ERR,并設(shè)置errno以指示錯誤原因。) F+ e- J+ J! O4 T2 [6 c) _
" A8 j9 e1 L+ i: g4 G以下是一個簡單的示例代碼,展示如何使用signal()函數(shù)來捕獲SIGINT信號,并執(zhí)行自定義的信號處理函數(shù):: `9 [6 j3 E3 e$ \$ ^, S
5 c! _) a9 _# i `! h% C
#include #include #include // 自定義信號處理函數(shù)void handle_signal(int signal) { printf("Caught signal %d) w4 {$ s! T" }& B# u! v6 v4 m
", signal);} int main() { // 將 SIGINT 信號處理方式設(shè)置為自定義的 handle_signal 函數(shù) signal(SIGINT, handle_signal); // 無限循環(huán),等待信號 while(1) { printf("Running...- s. z( [4 C) G! i, K
"); sleep(1); } return 0;}; B2 j* a" B. O' x9 I" T
在上述代碼中,當用戶按下CTRL+C(觸發(fā)SIGINT信號)時,自定義的handle_signal()函數(shù)會被調(diào)用,并輸出捕獲的信號編號。程序會繼續(xù)運行,而不會終止。如果要忽略SIGINT信號,可以將signal(SIGINT, handle_signal);改為signal(SIGINT, SIG_IGN);。+ f# Z& K! q& c* ]; u% D% N0 p
24 q# d2 a; W, v8 B9 g/ ^
sigaction() 函數(shù)
$ \, O( V1 Q0 _" C+ Hsigaction() 函數(shù)是 Linux 系統(tǒng)中用于設(shè)置信號處理方式的一個更強大且靈活的系統(tǒng)調(diào)用。與 signal() 函數(shù)相比,sigaction() 提供了更詳細的控制和更高的移植性,因此更推薦在實際開發(fā)中使用它。& e/ m0 y0 z9 ], P
! Y9 q/ ~: ]- f' D" T$ `
sigaction() 函數(shù)原型如下:9 H5 z, a; |4 S- d. l6 y
+ U: a! n5 d% f0 [# F" T7 W$ Y* y4 S3 o
#include int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
S' {6 l! u" R& O Q函數(shù)參數(shù):. h# y2 p4 }0 m: i+ h( q, j
signum:指定要設(shè)置處理方式的信號編號?梢詾槌 SIGKILL 和 SIGSTOP 以外的任何信號。act:指向 struct sigaction 結(jié)構(gòu)體的指針,用于指定信號的新的處理方式。如果 act 為 NULL,則不改變信號的處理方式。oldact:指向 struct sigaction 結(jié)構(gòu)體的指針,用于存儲信號先前的處理方式。如果不需要獲取原來的處理方式,可將其設(shè)置為 NULL。) h! J/ u) C: g
8 j7 K) p' L3 N3 a( U; g5 F. G6 B返回值:成功返回 0;失敗返回 -1,并設(shè)置 errno。% _9 M5 ~8 ^) R; }& M& r; r" a
/ E8 m/ i# L# `: p2 istruct sigaction 結(jié)構(gòu)體用于描述信號的處理方式,定義如下:& Y9 u+ q" s5 ?0 ^, o" h( a% C
# |) f% }+ O- Q7 n/ i0 h$ B6 kstruct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);};
4 I) q6 \, {& d, C2 R" y成員變量如下:5 k' |( u3 e5 w% {# l# W! Q' x
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! v3 X) z9 Q
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。& Y" u" J3 L# B% H$ V4 `
sa_restorer:已過時,通常不使用。
z3 f4 `" ]$ a2 Z
: V- c1 ^& T4 `, O: msiginfo_t 結(jié)構(gòu)體用于在 sa_sigaction 處理信號時傳遞更多的上下文信息,結(jié)構(gòu)體定義如下:; l: U N) ^) F( h
" N7 N1 A* w6 W4 J5 e6 {7 E8 g& l3 Utypedef 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;
( y$ v4 |; x6 T5 G) \下面是一個使用 sigaction() 捕獲 SIGINT 信號的示例代碼:; E% U( s* ]8 E7 Q) o t
/ L) d" @$ D5 g#include #include #include void handle_signal(int signal, siginfo_t *info, void *ucontext) { printf("Caught signal %d& o( X% ^" w# m2 E, ~! b
", signal); printf("Signal sent by process %d( R, e4 N! E9 [. \( `: O
", 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...
& ~( K' q5 _* Y0 i" Y& M"); sleep(1); } return 0;}
1 h4 M$ i6 X. F2 x3 Q& v( w. J在這段代碼中,sigaction() 用來設(shè)置 SIGINT 信號的處理方式。當用戶按下 CTRL+C 發(fā)送 SIGINT 信號時,程序會調(diào)用 handle_signal() 函數(shù),該函數(shù)可以通過 siginfo_t 結(jié)構(gòu)體獲取信號的更多信息,比如發(fā)送信號的進程 ID。
0 `6 o5 q# _7 ]# A2 N$ _3
6 U9 M3 K, B/ l; W注意事項* } o6 q) Q0 g+ W
當一個應(yīng)用程序剛啟動時,或在程序中未調(diào)用 signal() 或 sigaction() 來顯式設(shè)置信號處理方式時,進程對所有信號的處理方式通常為系統(tǒng)默認操作。這意味著大多數(shù)信號在未被特殊處理的情況下,都會執(zhí)行默認的處理動作。
% F) N* N" Q) {, z$ Z
! z" |& F% j! w+ A+ F% c當一個進程使用 fork() 系統(tǒng)調(diào)用創(chuàng)建一個子進程時,子進程會繼承父進程的信號處理方式。由于子進程是通過復(fù)制父進程的內(nèi)存映像而創(chuàng)建的,所以信號捕獲函數(shù)的地址在子進程中同樣有效。這意味著子進程將會繼承父進程的信號處理函數(shù)和其他相關(guān)的信號處理狀態(tài)。
( Z. c* E+ p9 b: A6 P2 c+ e1 C( S: V, x( K
這種繼承機制確保了子進程在初始狀態(tài)下能夠正確處理信號,避免因為未定義的信號處理而導(dǎo)致不可預(yù)測的行為。如果需要,子進程可以在運行過程中修改其信號處理方式,從而實現(xiàn)特定的行為需求。
2 B+ {6 B9 ~4 Q9 w1 }* d0 s
7 ?/ T/ m& t8 ~! b5 L2 r# _在設(shè)計信號處理函數(shù)時,通常建議保持其簡單性。這與設(shè)計中斷處理函數(shù)的原則相似:處理函數(shù)應(yīng)盡可能簡短和高效,避免執(zhí)行大量耗費 CPU 時間的操作。
; g4 x6 F0 Z; g9 o主要原因如下:" s& E" @, s) m0 g7 X' a
) C$ M. Q( K0 h0 Z減少信號競爭條件:信號競爭條件(Race Condition)指的是在多線程或多進程環(huán)境中,不同信號可能在不合適的時間內(nèi)打斷正在處理的代碼,導(dǎo)致不可預(yù)測的結(jié)果。如果信號處理函數(shù)復(fù)雜且耗時較長,進程在執(zhí)行處理函數(shù)時,可能會接收到相同或其他信號,增加競爭條件發(fā)生的風(fēng)險。保證系統(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ù)可以減少這些副作用。
: a! E/ \. U* c: |: o9 {7 y
3 H! ~/ a6 h& u1 R最佳實踐:
) v, s& R; f% u5 Z在信號處理函數(shù)中,只執(zhí)行必要的操作,如設(shè)置一個標志或記錄一個簡單的狀態(tài)。如果需要執(zhí)行復(fù)雜的邏輯,可以在信號處理函數(shù)中設(shè)置一個標志,然后在主程序的主循環(huán)中檢查該標志,并執(zhí)行相應(yīng)的復(fù)雜邏輯。$ H, t" v4 h: U9 ]8 A& p
這種方式可以有效分離信號處理與復(fù)雜邏輯,降低風(fēng)險。# ~' o$ O( V5 A0 a4 {
" e' q/ P/ k. }! |: A2 u0 q9 M
通過保持信號處理函數(shù)的簡單性,你可以有效提高程序的穩(wěn)定性和可靠性,減少潛在的問題和復(fù)雜的調(diào)試過程。! b; i" S% b6 N- G u
x s v' h- |
nvhmbzisgbe64011275505.jpg (71.14 KB, 下載次數(shù): 2)
下載附件
保存到相冊
nvhmbzisgbe64011275505.jpg
2024-8-31 11:41 上傳
! p& v! X& b. J7 a' H I
qis4hzruxuf64011275606.gif (45.46 KB, 下載次數(shù): 0)
下載附件
保存到相冊
qis4hzruxuf64011275606.gif
2024-8-31 11:41 上傳
1 J! u& ^4 K k( n: @# q
點擊閱讀原文,更精彩~ |
|