|
a4ewlgnzonr6403892317.gif (60.41 KB, 下載次數(shù): 0)
下載附件
保存到相冊
a4ewlgnzonr6403892317.gif
2024-9-4 09:45 上傳
& O' ?, p l( y4 A0 `
點擊上方藍(lán)色字體,關(guān)注我們& V7 }' S# M* `- n* G
在Linux系統(tǒng)中,當(dāng)進(jìn)程接收到信號后,可以通過設(shè)置信號處理方式來決定如何響應(yīng)信號。) W( N1 S& | E# K2 ` i8 Q
! H r- u7 y7 |! ^9 `8 ^- |
通常,信號的處理方式可以是以下三種之一:$ d- ^2 U, V/ Y* }
忽略信號:進(jìn)程對該信號不做任何處理,直接忽略。捕獲信號:為該信號設(shè)置一個處理函數(shù),當(dāng)信號到達(dá)時執(zhí)行該函數(shù)。執(zhí)行系統(tǒng)默認(rèn)操作:采用系統(tǒng)預(yù)定義的信號處理方式。6 @1 q3 _0 c' _# w3 _
1 h8 n9 G) O* k: g
本篇文章主要講解進(jìn)程如何處理信號。Linux 系統(tǒng)提供了兩個主要的函數(shù) signal() 和 sigaction() 用于設(shè)置信號的處理方式。! _7 |4 r# K$ _' f2 H
1
9 ~+ R# w- b- N8 j4 Asignal()函數(shù)# O& n) |1 }) l3 L. `
signal()函數(shù)的原型如下:
% n I8 ^9 A D3 ?# ~6 P* H2 p8 Y( }; z, Z
#include typedef void (*sig_t)(int); sig_t signal(int signum, sig_t handler); R p9 S9 ]; v; H* q% ]- [& }
函數(shù)參數(shù)和含義:' E; ]; X- O0 F! O; @$ Z
signum:指定需要進(jìn)行設(shè)置的信號。你可以使用信號的名稱(如SIGINT)或者其對應(yīng)的數(shù)字編號。不過,建議使用信號名稱,因為這樣可讀性更強(qiáng)。handler:這是一個sig_t類型的函數(shù)指針,用于指向信號的處理函數(shù)。handler可以設(shè)置為以下幾種:
! z7 H& @# o# }# T# [ }8 |用戶自定義函數(shù):這是一個處理函數(shù),在接收到信號時會自動調(diào)用這個函數(shù)。該函數(shù)的參數(shù)是一個int類型的值,表示觸發(fā)該函數(shù)的信號編號。通過這個參數(shù),你可以在一個函數(shù)中處理多個信號。SIG_IGN:表示忽略該信號,進(jìn)程在接收到該信號時不會進(jìn)行任何處理。SIG_DFL:表示采用系統(tǒng)的默認(rèn)處理方式,系統(tǒng)會對信號進(jìn)行其預(yù)定義的操作。
) S0 {6 C' l! \6 C; x" p6 _
7 f; j7 b4 Z4 ~+ W$ m) _ {返回值:signal()函數(shù)的返回值是一個sig_t類型的函數(shù)指針。成功調(diào)用時,返回指向之前信號處理函數(shù)的指針,這意味著你可以保存這個指針,以便在將來恢復(fù)原來的信號處理方式。如果調(diào)用失敗,則返回SIG_ERR,并設(shè)置errno以指示錯誤原因。
1 Q% H/ |. b+ @ g: @/ Q" @% v9 f
, z6 n3 I- v2 b7 L以下是一個簡單的示例代碼,展示如何使用signal()函數(shù)來捕獲SIGINT信號,并執(zhí)行自定義的信號處理函數(shù):
8 d* K3 d, k4 B' X2 h
3 t5 ? m4 ?, P- S8 F* v6 R9 ^* E#include #include #include // 自定義信號處理函數(shù)void handle_signal(int signal) { printf("Caught signal %d+ B, h" w* g7 ?
", signal);} int main() { // 將 SIGINT 信號處理方式設(shè)置為自定義的 handle_signal 函數(shù) signal(SIGINT, handle_signal); // 無限循環(huán),等待信號 while(1) { printf("Running...
0 T3 d0 i. N$ U. S) O/ S$ G8 T"); sleep(1); } return 0;}* i% }% {' L. w- r2 ?' r
在上述代碼中,當(dāng)用戶按下CTRL+C(觸發(fā)SIGINT信號)時,自定義的handle_signal()函數(shù)會被調(diào)用,并輸出捕獲的信號編號。程序會繼續(xù)運(yùn)行,而不會終止。如果要忽略SIGINT信號,可以將signal(SIGINT, handle_signal);改為signal(SIGINT, SIG_IGN);。0 U( Z3 z5 p I. B6 ^, `+ z
2
6 O" d3 G& E2 q0 c5 Bsigaction() 函數(shù)
; `, L1 O! v0 m4 w1 \sigaction() 函數(shù)是 Linux 系統(tǒng)中用于設(shè)置信號處理方式的一個更強(qiáng)大且靈活的系統(tǒng)調(diào)用。與 signal() 函數(shù)相比,sigaction() 提供了更詳細(xì)的控制和更高的移植性,因此更推薦在實際開發(fā)中使用它。. H& w2 ?3 Z$ }6 t, Z/ k* b* ?
, g8 O2 E8 \2 R; A* I
sigaction() 函數(shù)原型如下: k* H/ d& c& e7 o- [
7 T$ @- {9 Z+ C3 \8 w. H5 i# E
#include int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
" C2 @* v3 m2 w$ I/ W+ ^5 w' r' F函數(shù)參數(shù):
* I H/ n% r: e. @" y% U4 ysignum:指定要設(shè)置處理方式的信號編號。可以為除 SIGKILL 和 SIGSTOP 以外的任何信號。act:指向 struct sigaction 結(jié)構(gòu)體的指針,用于指定信號的新的處理方式。如果 act 為 NULL,則不改變信號的處理方式。oldact:指向 struct sigaction 結(jié)構(gòu)體的指針,用于存儲信號先前的處理方式。如果不需要獲取原來的處理方式,可將其設(shè)置為 NULL。0 v2 q1 c, w" k+ [9 [- v
! I; `, B9 a9 E; u( x返回值:成功返回 0;失敗返回 -1,并設(shè)置 errno。: Z0 U+ R8 l8 F8 x) }8 ^
: T* }$ T6 A! C* W# Estruct sigaction 結(jié)構(gòu)體用于描述信號的處理方式,定義如下:3 ^, X# X1 J8 U0 [" J. @% L
! z# s+ Y* |7 D, p- p; Lstruct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);};
: K. e7 U* m$ o- {0 }成員變量如下:2 f1 B8 b, Z% w0 ~8 k5 m
sa_handler:信號處理函數(shù)指針,與 signal() 函數(shù)中的 handler 參數(shù)相同?稍O(shè)置為自定義函數(shù)、SIG_IGN(忽略信號)或 SIG_DFL(系統(tǒng)默認(rèn)處理)。sa_sigaction:另一個信號處理函數(shù)指針,用于處理帶有更多信息的信號。與 sa_handler 互斥,通常使用 sa_handler。選擇使用 sa_sigaction 需設(shè)置 SA_SIGINFO 標(biāo)志。sa_mask:定義在執(zhí)行信號處理函數(shù)期間要阻塞的信號集合,以避免信號之間的競爭條件。sa_flags:標(biāo)志位,用于控制信號的處理行為。常用標(biāo)志包括:: \% \& U& K7 r# E2 Z, u9 Q
SA_NOCLDSTOP:阻止當(dāng)子進(jìn)程停止或恢復(fù)時發(fā)送 SIGCHLD 信號。SA_NOCLDWAIT:子進(jìn)程終止時不變?yōu)榻┦M(jìn)程。SA_NODEFER:不阻塞自身的信號。SA_RESETHAND:執(zhí)行完信號處理后將信號恢復(fù)為默認(rèn)處理方式。SA_RESTART:被信號中斷的系統(tǒng)調(diào)用在信號處理完成后重新發(fā)起。SA_SIGINFO:使用 sa_sigaction 代替 sa_handler。7 T7 ^% ~3 d4 Y3 G5 c
sa_restorer:已過時,通常不使用。
& Y* p! }0 d6 o5 I# t
. Q4 X( K t. N) m0 W9 |* w* zsiginfo_t 結(jié)構(gòu)體用于在 sa_sigaction 處理信號時傳遞更多的上下文信息,結(jié)構(gòu)體定義如下:
! z. I- t k& ]: i- M9 J& F4 b3 n4 g! K5 G# O6 i* p. g- H
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;0 ]' m6 Z% {1 o+ O: x2 X; g
下面是一個使用 sigaction() 捕獲 SIGINT 信號的示例代碼:
/ ~. l0 [ h& F, C* R! E! }) c! p0 t5 O4 C; _
#include #include #include void handle_signal(int signal, siginfo_t *info, void *ucontext) { printf("Caught signal %d/ `( ], [) `- D( w" `+ v7 r
", signal); printf("Signal sent by process %d9 F: b0 {+ s" g7 Q, E/ ^. x
", 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...! `, v9 z9 a6 [+ ?* M8 {$ l3 n
"); sleep(1); } return 0;}
: Y; E i4 Z* f( ]4 b在這段代碼中,sigaction() 用來設(shè)置 SIGINT 信號的處理方式。當(dāng)用戶按下 CTRL+C 發(fā)送 SIGINT 信號時,程序會調(diào)用 handle_signal() 函數(shù),該函數(shù)可以通過 siginfo_t 結(jié)構(gòu)體獲取信號的更多信息,比如發(fā)送信號的進(jìn)程 ID。
" u& q4 |6 H7 D) F8 d1 ?35 z8 I5 z- E, A& s& e
注意事項% o( L" o" n7 h# \6 Y5 g
當(dāng)一個應(yīng)用程序剛啟動時,或在程序中未調(diào)用 signal() 或 sigaction() 來顯式設(shè)置信號處理方式時,進(jìn)程對所有信號的處理方式通常為系統(tǒng)默認(rèn)操作。這意味著大多數(shù)信號在未被特殊處理的情況下,都會執(zhí)行默認(rèn)的處理動作。
6 b$ D" l/ ?) e* v' ^. |
( `! t2 `& h6 ^0 v% v7 x* e. O當(dāng)一個進(jìn)程使用 fork() 系統(tǒng)調(diào)用創(chuàng)建一個子進(jìn)程時,子進(jìn)程會繼承父進(jìn)程的信號處理方式。由于子進(jìn)程是通過復(fù)制父進(jìn)程的內(nèi)存映像而創(chuàng)建的,所以信號捕獲函數(shù)的地址在子進(jìn)程中同樣有效。這意味著子進(jìn)程將會繼承父進(jìn)程的信號處理函數(shù)和其他相關(guān)的信號處理狀態(tài)。
4 L- ^* P# n$ |; E0 H* ^
( H$ x) x! Z# [這種繼承機(jī)制確保了子進(jìn)程在初始狀態(tài)下能夠正確處理信號,避免因為未定義的信號處理而導(dǎo)致不可預(yù)測的行為。如果需要,子進(jìn)程可以在運(yùn)行過程中修改其信號處理方式,從而實現(xiàn)特定的行為需求。
0 i5 x4 C2 J/ C2 G, f
# K, l9 @2 a0 |, w: X6 `在設(shè)計信號處理函數(shù)時,通常建議保持其簡單性。這與設(shè)計中斷處理函數(shù)的原則相似:處理函數(shù)應(yīng)盡可能簡短和高效,避免執(zhí)行大量耗費(fèi) CPU 時間的操作。
4 r0 _: {$ i' T% \! _6 k主要原因如下:
1 }) i) O* k J: J- Y
# L" H/ Q; e& N減少信號競爭條件:信號競爭條件(Race Condition)指的是在多線程或多進(jìn)程環(huán)境中,不同信號可能在不合適的時間內(nèi)打斷正在處理的代碼,導(dǎo)致不可預(yù)測的結(jié)果。如果信號處理函數(shù)復(fù)雜且耗時較長,進(jìn)程在執(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ù)可能會改變進(jìn)程的全局狀態(tài)(如修改全局變量),這可能會導(dǎo)致進(jìn)程在信號處理完成后進(jìn)入不一致的狀態(tài)。因此,簡單的處理函數(shù)可以減少這些副作用。
]$ }5 F0 |: ?9 V5 k8 e( T: Y3 u6 e' I/ n6 n
最佳實踐:
w5 i! E& V% m' N在信號處理函數(shù)中,只執(zhí)行必要的操作,如設(shè)置一個標(biāo)志或記錄一個簡單的狀態(tài)。如果需要執(zhí)行復(fù)雜的邏輯,可以在信號處理函數(shù)中設(shè)置一個標(biāo)志,然后在主程序的主循環(huán)中檢查該標(biāo)志,并執(zhí)行相應(yīng)的復(fù)雜邏輯。
0 k: w3 U' g* p _( }這種方式可以有效分離信號處理與復(fù)雜邏輯,降低風(fēng)險。( W6 z8 v0 K& C% y) ^* d
* M6 @" X/ m1 z& j通過保持信號處理函數(shù)的簡單性,你可以有效提高程序的穩(wěn)定性和可靠性,減少潛在的問題和復(fù)雜的調(diào)試過程。
: {4 L# m1 I# o6 z2 O
$ O' p+ }, W1 d) ~ }2 o
va2yniooshi6403892417.jpg (71.14 KB, 下載次數(shù): 0)
下載附件
保存到相冊
va2yniooshi6403892417.jpg
2024-9-4 09:45 上傳
% n/ T. [( @; g! F
fh0opeph22a6403892517.gif (45.46 KB, 下載次數(shù): 0)
下載附件
保存到相冊
fh0opeph22a6403892517.gif
2024-9-4 09:45 上傳
5 t( L1 y7 m5 r6 N# R
點擊閱讀原文,更精彩~ |
|