信号捕捉(signal、sigaction)
信號(hào)的基本屬性:軟中斷,由內(nèi)核發(fā)送,內(nèi)核處理。某個(gè)進(jìn)程通過(guò)內(nèi)核向另一個(gè)進(jìn)程發(fā)送信號(hào)時(shí)(引起信號(hào)產(chǎn)生的五個(gè)因素),另一個(gè)進(jìn)程將會(huì)陷入內(nèi)核進(jìn)行中斷處理,未決信號(hào)集中相應(yīng)信號(hào)置1,當(dāng)遞達(dá)后,置0。如果阻塞信號(hào)集相應(yīng)信號(hào)為1,則該信號(hào)處于未決狀態(tài)。處于未決狀態(tài)中的信號(hào),多次發(fā)送時(shí),只是執(zhí)行一次,因?yàn)樵谖礇Q信號(hào)集中只是記錄了該信號(hào)的狀態(tài),沒(méi)有記錄發(fā)送的次數(shù)。信號(hào)抵達(dá)后,內(nèi)核進(jìn)行處理。處理方式有三:默認(rèn)處理方式(5種);忽略(丟棄)和捕捉。下面說(shuō)明捕捉機(jī)制。
signal和sigaction函數(shù)只是完成對(duì)一個(gè)信號(hào)進(jìn)行注冊(cè)的功能,而對(duì)信號(hào)的捕捉的處理都是由內(nèi)核完成的。當(dāng)對(duì)一個(gè)信號(hào)進(jìn)行注冊(cè)后,內(nèi)核對(duì)其捕捉同時(shí)調(diào)用其注冊(cè)時(shí)對(duì)應(yīng)的用戶處理函數(shù)。
(1)signal函數(shù)
typedef void (*sighandler_t)(int);? //定義一個(gè)函數(shù)類(lèi)型 sighandler_t
sighandler_t signal(int signum, sighandler_t handler);
作用:注冊(cè)一個(gè)信號(hào)捕捉函數(shù)
返回值:成功返回sighandler_t類(lèi)型的函數(shù)(或函數(shù)首地址);失敗則返回一個(gè)宏:SIG_ERR。注意判斷該函數(shù)的返回值: sighandler ret = signal(·······);if(ret==SIG_ERR)
第一個(gè)參數(shù)為信號(hào);第二個(gè)參數(shù)為sighandler_t類(lèi)型函數(shù)(即返回值為void,形參為int)。
注意:該函數(shù)由ANSI定義,由于歷史原因在不同版本的Unix和不同版本的Linux中可能有不同的行為。因此應(yīng)該盡量避免使用它,取而代之使用sigaction函數(shù)。
#include <signal.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <unistd.h>typedef void (*sighandler_t) (int); //定義sighandler_t類(lèi)型void catchsigint(int signo) {printf("-----------------catch\n"); }int main(void) {sighandler_t handler;handler = signal(SIGINT, catchsigint); //注冊(cè)2號(hào)信號(hào)if (handler == SIG_ERR) {perror("signal error");exit(1);} //判斷返回值while (1);return 0; }[root@localhost 01_signal_test]# ./signal2
^C-----------------catch???????????????? //Ctrl+C?
^C-----------------catch??????????? ?????//Ctrl+C?
^\Quit (core dumped) ????????????????//Ctrl+\
只要一發(fā)送2號(hào)信號(hào),就會(huì)執(zhí)行相應(yīng)函數(shù)。
(2)sigaction函數(shù)
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);?
作用:對(duì)某個(gè)信號(hào)進(jìn)行注冊(cè)(同signal),即對(duì)某個(gè)信號(hào)之前對(duì)應(yīng)的處理方式(函數(shù))進(jìn)行修改。
返回值:成功0;失敗-1,設(shè)置errno。
參數(shù):
act:傳入?yún)?shù),新的處理方式。
oldact:傳出參數(shù),舊的處理方式。
struct sigaction結(jié)構(gòu)體:
??? struct sigaction {
?????? ?void??? ?(*sa_handler)(int);
??????? void???? (*sa_sigaction)(int, siginfo_t *, void *);
????? ??sigset_t?? sa_mask;
??????? int?????? sa_flags;
?????? ?void???? (*sa_restorer)(void);
??? };? ?//最后一個(gè)成員不用(舍棄了);第二成員不常用
sa_restorer:該元素是過(guò)時(shí)的,不應(yīng)該使用,POSIX.1標(biāo)準(zhǔn)將不指定該元素。(棄用)
sa_sigaction:當(dāng)sa_flags被指定為SA_SIGINFO標(biāo)志時(shí),使用該信號(hào)處理程序。(很少使用)?
重點(diǎn)掌握:
sa_handler:指定信號(hào)捕捉后的處理函數(shù)名(即注冊(cè)函數(shù))。也可賦值為SIG_IGN表忽略 或 SIG_DFL表執(zhí)行默認(rèn)動(dòng)作;
sa_mask: 調(diào)用信號(hào)處理函數(shù)時(shí),所要屏蔽的信號(hào)集合(信號(hào)屏蔽字)。注意:僅在處理函數(shù)被調(diào)用期間屏蔽生效,是臨時(shí)性設(shè)置;sa_mask也是一個(gè)字(64位),只是在執(zhí)行相應(yīng)的用戶處理函數(shù)期間生效。即在執(zhí)行用戶處理函數(shù)期間, sa_mask屏蔽的信號(hào)也不能遞達(dá),處于未決狀態(tài)。如果sa_mask未屏蔽,則響應(yīng)信號(hào),中斷嵌套。相當(dāng)于此期間,sa_mask代替了mask。
sa_flags:通常設(shè)置為0,表示用默認(rèn)屬性。默認(rèn)屬性即為:sa_mask中將自己屏蔽,即該信號(hào)的注冊(cè)函數(shù)執(zhí)行期間,再次向進(jìn)程發(fā)送該信號(hào),該信號(hào)不能遞達(dá),處于未決狀態(tài)。
最后一個(gè)參數(shù)如果不關(guān)心之前的處理方式,可以為NULL。
(3)信號(hào)捕捉機(jī)制
進(jìn)程正常運(yùn)行時(shí),默認(rèn)PCB中有一個(gè)信號(hào)屏蔽字,假定為mask,它決定了進(jìn)程自動(dòng)屏蔽哪些信號(hào)。當(dāng)注冊(cè)了某個(gè)信號(hào)捕捉函數(shù),捕捉到該信號(hào)以后,要調(diào)用該函數(shù)。而該函數(shù)有可能執(zhí)行很長(zhǎng)時(shí)間,在這期間所屏蔽的信號(hào)不由mask來(lái)指定。而是用sa_mask來(lái)指定。調(diào)用完信號(hào)處理函數(shù),再恢復(fù)為mask。
sa_flags為0時(shí),XXX信號(hào)捕捉函數(shù)執(zhí)行期間,XXX信號(hào)自動(dòng)被屏蔽。
阻塞的常規(guī)信號(hào)(1-31)不支持排隊(duì),產(chǎn)生多次只記錄一次。(后32個(gè)實(shí)時(shí)信號(hào)支持排隊(duì))
內(nèi)核實(shí)現(xiàn)信號(hào)捕捉的過(guò)程如下:
首先,處于用戶態(tài)(user mode)的某個(gè)進(jìn)程在執(zhí)行到某個(gè)指令時(shí)突然接收某個(gè)信號(hào)(軟中斷,終端按鍵產(chǎn)生;硬件異常產(chǎn)生;命令產(chǎn)生;系統(tǒng)調(diào)用產(chǎn)生或者軟件條件產(chǎn)生),會(huì)暫停執(zhí)行下一條指令而陷入內(nèi)核進(jìn)入內(nèi)核態(tài)。
內(nèi)核在處理這一異常后,在準(zhǔn)備會(huì)用戶態(tài)之前先處理可以遞達(dá)該進(jìn)程的信號(hào)。
如果該信號(hào)的處理方式為捕捉,則內(nèi)核對(duì)該信號(hào)進(jìn)行捕捉,同時(shí)調(diào)用相應(yīng)的用戶處理函數(shù),回到用戶態(tài)執(zhí)行相應(yīng)的用戶處理函數(shù)(注意不是回到主控制流程)。
在用戶處理函數(shù)執(zhí)行完返回時(shí),再次執(zhí)行系統(tǒng)調(diào)用sigretum再次進(jìn)入內(nèi)核。因?yàn)楹瘮?shù)執(zhí)行完需要返回到該函數(shù)的調(diào)用點(diǎn),而該函數(shù)是內(nèi)核調(diào)用的,因此需要再次返回到內(nèi)核。
最后,從內(nèi)核再次返回到用戶模式,從上次中斷處繼續(xù)執(zhí)行下一條指令。
?
//練習(xí)1:為某個(gè)信號(hào)設(shè)置捕捉函數(shù);驗(yàn)證在信號(hào)處理函數(shù)執(zhí)行期間,該信號(hào)多次遞送,那么只在處理函數(shù)之行結(jié)束后,處理一次;驗(yàn)證sa_mask在捕捉函數(shù)執(zhí)行期間的屏蔽作用。
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h>void docatch(int signo) //用戶處理函數(shù) {printf("the %dth signal is catched\n", signo);sleep(10); printf("-------finish------\n"); } int main(void) {int ret;struct sigaction act;act.sa_handler = docatch;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask, SIGQUIT); //sa_mask屏蔽字中,3號(hào)信號(hào)置1act.sa_flags = 0; //默認(rèn)屬性 信號(hào)捕捉函數(shù)執(zhí)行期間,自動(dòng)屏蔽本信號(hào)ret = sigaction(SIGINT, &act, NULL); //注冊(cè)2號(hào)信號(hào)if (ret == -1) {perror("sigaction error");exit(1);}while (1);return 0; }[root@localhost 01_signal_test]# ./test_sigac
^Cthe 2th signal is catched ??// 發(fā)2號(hào)信號(hào) Ctrl +C
-------finish------
^Cthe 2th signal is catched ?// 發(fā)2號(hào)信號(hào) Ctrl +C
^C^C^C^C^C^C^C^C^C^C^C^C^C-------finish------? ?// 執(zhí)行期間,發(fā)多個(gè)2號(hào)信號(hào)
the 2th signal is catched
-------finish------???????? ???//但是只是執(zhí)行了一次
^Cthe 2th signal is catched
^\^\^\^\^\^\^\^\^\^\^\^\-------finish------?? // 執(zhí)行期間,發(fā)多個(gè)3號(hào)信號(hào)
Quit (core dumped)?? //2號(hào)信號(hào)處理完后,處理2號(hào),則退出進(jìn)程,結(jié)束。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的信号捕捉(signal、sigaction)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: lol维克多为什么出面具
- 下一篇: 时序竞态(竞态条件)