如何在Linux下捕捉信號
信號由三種處理方式:
- 忽略
- 執(zhí)行該信號的默認(rèn)處理動作
- 捕捉信號
如果信號的處理動作是用戶自定義函數(shù),在信號遞達(dá)時就調(diào)用這個自定義函數(shù),這稱為捕捉信號。
進(jìn)程收到一個信號后不會被立即處理,而是在恰當(dāng)時機(jī)進(jìn)行處理!即內(nèi)核態(tài)返回用戶態(tài)之前 !
但是由于信號處理函數(shù)的代碼在用戶空間,所以這增加了內(nèi)核處理信號捕捉的復(fù)雜度。
內(nèi)核實現(xiàn)信號捕捉的步驟:
- 用戶為某信號注冊一個信號處理函數(shù)sighandler。
- 當(dāng)前正在執(zhí)行主程序,這時候因為中斷、異常或系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài)。
- 在處理完異常要返回用戶態(tài)的主程序之前,檢查到有信號未處理,并發(fā)現(xiàn)該信號需要按照用戶自定義的函數(shù)來處理。
- 內(nèi)核決定返回用戶態(tài)執(zhí)行sighandler函數(shù),而不是恢復(fù)main函數(shù)的上下文繼續(xù)執(zhí)行!(sighandler和main函數(shù)使用的是不同的堆棧空間,它們之間不存在調(diào)用和被調(diào)用的關(guān)系,是兩個獨立的控制流程)
- sighandler函數(shù)返回后,執(zhí)行特殊的系統(tǒng)調(diào)用sigreturn從用戶態(tài)回到內(nèi)核態(tài)
- 檢查是否還有其它信號需要遞達(dá),如果沒有 則返回用戶態(tài)并恢復(fù)主程序的上下文信息繼續(xù)執(zhí)行。
signal
給某一個進(jìn)程的某一個信號(標(biāo)號為signum)注冊一個相應(yīng)的處理函數(shù),即對該信號的默認(rèn)處理動作進(jìn)行修改,修改為handler函數(shù)指向的方式;
- #include <signal.h>
- typedef void (*sighandler_t)(int);
- sighandler_t signal(int signum, sighandler_t handler);<br>//即:<br>void (*signal(int, void(*)(int)))(int);
signal函數(shù)接受兩個參數(shù):一個整型的信號編號,以及一個指向用戶定義的信號處理函數(shù)的指針。
此外,signal函數(shù)的返回值是一個指向調(diào)用用戶定義信號處理函數(shù)的指針。
sigaction
sigaction函數(shù)可以讀取和修改與指定信號相關(guān)聯(lián)的處理動作。
- #include <signal.h>
- int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- struct sigaction
- {
- void (*sa_handler)(int); //信號處理方式
- void (*sa_sigaction)(int, siginfo_t *, void *); //實時信號的處理方式 暫不討論
- sigset_t sa_mask; //額外屏蔽的信號
- int sa_flags;
- void (*sa_restorer)(void);
- };
signum是指定信號的編號。
處理方式:
若act指針非空,則根據(jù)act結(jié)構(gòu)體中的信號處理函數(shù)來修改該信號的處理動作。
若oact指針非 空,則通過oact傳出該信號原來的處理動作。
現(xiàn)將原來的處理動作備份到oact里,然后根據(jù)act修改該信號的處理動作。
(注:后兩個參數(shù)都是輸入輸出型參數(shù)!)
將sa_handler三種可選方式:
- 賦值為常數(shù)SIG_IGN傳給sigaction表示忽略信號;
- 賦值為常數(shù)SIG_DFL表示執(zhí)行系統(tǒng)默認(rèn)動作;
- 賦值為一個函數(shù)指針表示用自定義函數(shù)捕捉信號,或者說向內(nèi)核注冊一個信號處理函 數(shù),該函數(shù)返回值為void,可以帶一個int參數(shù),通過參數(shù)可以得知當(dāng)前信號的編號,這樣就可以用同一個函數(shù)處理多種信號。
(注:這是一個回調(diào)函數(shù),不是被main函數(shù)調(diào)用,而是被系統(tǒng)所調(diào)用)
當(dāng)某個信號的處理函數(shù)被調(diào)用時,內(nèi)核自動將當(dāng)前信號加入進(jìn)程的信號屏蔽字,當(dāng)信號處理函數(shù)返回時自動恢復(fù)原來的信號屏蔽字,這樣就保證了在處理某個信號時,如果這種信號再次產(chǎn)生,那么 它會被阻塞到當(dāng)前處理結(jié)束為止。
pause
pause函數(shù)使調(diào)用進(jìn)程掛起直到有信號遞達(dá)!
- #include <unistd.h>
- int pause(void);
處理方式:
如果信號的處理動作是終止進(jìn)程,則進(jìn)程終止,pause函數(shù)沒有機(jī)會返回;
如果信號的處理動作是忽略,則進(jìn)程繼續(xù)處于掛起狀態(tài),pause不返回;
如果信號的處理動作是捕捉,則調(diào)用了信號處理函數(shù)之后pause返回-1,errno設(shè)置為EINTR。
所以pause只有出錯的返回值(類似exec函數(shù)家族)。錯誤碼EINTR表示“被信號中斷”。
舉個栗子
- 定義一個鬧鐘,約定times秒后,內(nèi)核向該進(jìn)程發(fā)送一個SIGALRM信號;
- 調(diào)用pause函數(shù)將進(jìn)程掛起,內(nèi)核切換到別的進(jìn)程運行;
- times秒后,內(nèi)核向該進(jìn)程發(fā)送SIGALRM信號,發(fā)現(xiàn)其處理動作是一個自定義函數(shù),于是切回用戶態(tài)執(zhí)行該自定義處理函數(shù);
- 進(jìn)入sig_alrm函數(shù)時SIGALRM信號被自動屏蔽,從sig_alrm函數(shù)返回時SIGALRM信號自動解除屏蔽。然后自動執(zhí)行特殊的系統(tǒng)調(diào)用sigreturn再次進(jìn)入內(nèi)核,之后再返回用戶態(tài)繼續(xù)執(zhí)行進(jìn)程的主控制流程(main函數(shù)調(diào)用的mytest函數(shù))。
- pause函數(shù)返回-1,然后調(diào)用alarm(0)取消鬧鐘,調(diào)用sigaction恢復(fù)SIGALRM信號以前的處理 動作。
- /*************************************************************************
- > File Name: Pause.c
- > Author:Lynn-Zhang
- > Mail: iynu17@yeah.net
- > Created Time: Sun 14 Aug 2016 12:27:03 PM CST
- ************************************************************************/
- #include<stdio.h>
- #include<signal.h>
- #include<unistd.h>
- void sig_alarm(int signum)
- {
- printf("I am a custom handler!\n");
- }
- void mysleep(unsigned int times)
- {
- //注冊兩個信號處理動作
- struct sigaction new,old;
- new.sa_handler=sig_alarm; //信號處理函數(shù)
- sigemptyset(&new.sa_mask);//不屏蔽任何信號屏蔽字
- new.sa_flags=0;
- //對SIGALRM 信號的默認(rèn)處理動作修改為自定義處理動作
- sigaction(SIGALRM,&new,&old);
- alarm(times);
- pause(); //掛起等待
- alarm(1);
- sleep(2);
- alarm(0); //取消鬧鐘
- //恢復(fù)SIGALRM 信號到默認(rèn)處理動作
- sigaction(SIGALRM,&old,NULL);
- alarm(1);
- sleep(2);
- }
- int main()
- {
- while(1)
- {
- mysleep(2);
- printf("many seconds passed\n");
- printf("###################\n");
- }
- return 0;
- }
定義一個鬧鐘并掛起等待,收到信號后執(zhí)行自定義處理動作,在沒有恢復(fù)默認(rèn)處理動作前,收到SIGALRM信號都會按照其自定義處理函數(shù)來處理。恢復(fù)自定義處理動作之后收到SIGALRM信號則執(zhí)行其默認(rèn)處理動作即終止進(jìn)程!




























