進(jìn)程間通信之信號
時間:2018-03-13作者:華清遠(yuǎn)見
一:信號的基本介紹 信號是在軟件層次上對中斷機(jī)制的一種模擬,是一種異步通信方式(進(jìn)程在運(yùn)行過程中,隨時可能被各種信號打斷)。 信號可以直接進(jìn)行用戶空間進(jìn)程和內(nèi)核進(jìn)程之間的交互,內(nèi)核進(jìn)程也可以利用它來通知用戶空間進(jìn)程發(fā)生了那些系統(tǒng)事件。 如果該進(jìn)程當(dāng)前并未處于執(zhí)行態(tài),則該信號就由內(nèi)核保存起來,直到該進(jìn) 程恢復(fù)執(zhí)行再傳遞個它;如果一個信號被進(jìn)程設(shè)置為阻塞,則該信號的傳遞被延遲,直到其阻塞取消時才被傳遞給進(jìn)程。 二:信號的產(chǎn)生 A.用戶在終端按下某些鍵時,終端驅(qū)動程序會發(fā)送信號給前臺進(jìn)程,例如ctr+c產(chǎn)生SIGINT, ctr + \產(chǎn)生SIGQUI信號,ctr + z產(chǎn)生SIGTSTP。 B.硬件異常產(chǎn)生信號,這些條件由硬件檢測到并通知內(nèi)核,然后內(nèi)核向當(dāng)前進(jìn)程發(fā)送適當(dāng)?shù)男盘。例如?dāng)前進(jìn)程執(zhí)行了除以0的指令,CPU的運(yùn)算單元會產(chǎn)生異常,內(nèi)核將這個異常解釋為SIGFPE信號發(fā)送給進(jìn)程。再比如當(dāng)前進(jìn)程訪問了非法內(nèi)存地址,MMU會產(chǎn)生異常,內(nèi)核將這個異常解釋為SIGSEGV信號發(fā)送給當(dāng)前進(jìn)程 。我們常見的段錯誤。 C.一個進(jìn)程調(diào)用int kill(pid_t pid,int sig)函數(shù)可以給另一個進(jìn)程發(fā)送信號。 D.可以用kill命令給某個進(jìn)程發(fā)送信號,如果不明確指定信號則發(fā)送SIGTERM信號,該信號的默認(rèn)處理動作是終止進(jìn)程。 E.當(dāng)內(nèi)核檢測到某種軟件條件發(fā)生時也可以通過信號通知進(jìn)程,例如鬧鐘超時產(chǎn)生 SIGALRM信號,向讀端已關(guān)閉的管道寫數(shù)據(jù)時產(chǎn)生SIGPIPE信號。 三:linux操作系統(tǒng)支持的信號 A. kill -l命令查看當(dāng)前系統(tǒng)支持的所有的信號
B:常用信號的含義
四:linux中進(jìn)程對信號處理 忽略信號,即對信號不做任何處理,但是有兩個信號不能忽略:即SIGKILL及 SIGSTOP。 捕捉信號,定義并注冊信號處理函數(shù),當(dāng)信號發(fā)生時,執(zhí)行相應(yīng)的處理函數(shù)。 【重點(diǎn)】。 執(zhí)行缺省操作,Linux對每種信號都規(guī)定了默認(rèn)操作 五:相關(guān)API
1:信號的發(fā)送(kill和raise) 函數(shù)原型:int kill(pid_t pid, int sig); 函數(shù)功能:給進(jìn)程 id 為 pid 的進(jìn)程發(fā)送信號 函數(shù)參數(shù):@param pid : 發(fā)送信號的目標(biāo)進(jìn)程的 id
@param sig : 發(fā)送的信號編號,例如:9(SIGKILL) 返回值:成功調(diào)用返回 0 ,失敗返回 -1 ,并設(shè)置 errno 函數(shù)原型:int raise(int sig); 函數(shù)功能:給當(dāng)前進(jìn)程自己發(fā)送信號 函數(shù)參數(shù):@param sig : 發(fā)送的信號編號 返回值:成功調(diào)用返回 0 ,失敗返回 -1 ,并設(shè)置 errno .*練習(xí) 我們通過終端kill -9 某個進(jìn)程終止過一個進(jìn)程,現(xiàn)在我們使用kill函數(shù)來終止一下。過程: 父進(jìn)程創(chuàng)建一個子進(jìn)程,父進(jìn)程拿到子進(jìn)程的進(jìn)程ID;子進(jìn)程中while循環(huán)打印hello,sleep(1); 父進(jìn)程sleep(5)之后,給子進(jìn)程發(fā)送9這個信號來終止子進(jìn)程。 2:信號的捕捉(signal) 知識點(diǎn)回顧 void func(int);//函數(shù)的聲明 void (*func)(int);//定義函數(shù)指針,指向void (int)類型的函數(shù)typedef int a;//給int類型的a起別名 typedef void (*funcp)(int);//給類型為void (*)(int);的函數(shù)指針起別名funcp 捕捉信號的處理過程:
#include <signal.h> typedef void (*sighandler_t)(int);//指向函數(shù)的指針,表示信號處理函數(shù)的形式sighandler_t signal(int signum, sighandler_t handler); 函數(shù)功能 : 將信號與信號處理函數(shù)進(jìn)行關(guān)聯(lián)函數(shù)參數(shù):@param signum : 信號的編號 @param handler : 信號處理函數(shù)的指針SIG_DFL : 表示默認(rèn)操作 SIG_IGN : 表示忽略信號(SIGKILL和SIGSTOP時不能被忽略的) 返回值:成功調(diào)用返回信號處理函數(shù)的指針,否則,返回SIG_ERR
注意:sighandler_t handler中的int保存的是調(diào)用這個函數(shù)是因為哪個信號觸發(fā)的,帶過來對應(yīng)的信號值。 .* 練習(xí) (1)忽略ctrl+c對進(jìn)程的終止信號。signal(SIGINT, SIG_IGN); (2)在信號處理函數(shù)中將對應(yīng)的信號的描述信息進(jìn)行打印。 .* 練習(xí): fork前采用signal信號處理函數(shù)不阻塞,不輪詢的方式回收僵尸態(tài)子進(jìn)程[waitpid()函數(shù)]。 在信號處理函數(shù)signal_handler()中對信號進(jìn)行收尸操作。然后利用fork函數(shù)創(chuàng)建一個子進(jìn)程。休眠10s后退出。父進(jìn)程是一個死循環(huán),每秒輸出"father do something…"的字符串。 提示: 子進(jìn)程在終止時會給父進(jìn)程發(fā)SIGCHLD,該信號的默認(rèn)處理動作是忽略,父進(jìn)程可以自定義SIGCHLD信號的處理函數(shù)。我們這里調(diào)用waitpid非阻塞的回收僵尸態(tài)子進(jìn)程。這樣父進(jìn)程只需要專心處理自己的工作,不必關(guān)心子進(jìn)程了,子進(jìn)程終止時會通知父進(jìn)程,父進(jìn)程在信號處理函數(shù)中調(diào)用waitpid函數(shù)清理子進(jìn)程即可。 一般信號對僵尸態(tài)子進(jìn)程的處理方法: <1>父進(jìn)程采用signal(SIGCHLD, hand_signal),采用信號處理函數(shù),對接收到的SIGCHLD進(jìn)行進(jìn)行處理。在接收到SIGCHLD信號的時候,采用waitpid利用非阻塞的方式的釋放它們的資源。若是使用wait()函數(shù)的話,父進(jìn)程會阻塞。 [推薦使用] <2>父進(jìn)程采用signal(SIGCHLD, SIG_IGN),忽略SIGCHLD信號,這樣子進(jìn)程結(jié)束后,就不需要父進(jìn)程來wait和釋放資源。它會自動被過繼給老祖宗init進(jìn)程,int進(jìn)程會負(fù)責(zé)釋放他的資源,這樣就不會產(chǎn)生僵尸態(tài)子進(jìn)程。 3:定時鬧鐘函數(shù)(alarm) unsigned int alarm(unsigned int seconds); 函數(shù)功能:給進(jìn)程啟動一個定時器,經(jīng)過seconds秒后把SIGALRM信號發(fā)送給當(dāng)前進(jìn)程。函數(shù)參數(shù):@seconds 秒 返回值:成功返回0,失敗返回 -1 注意:一個進(jìn)程只能有一個鬧鐘事件,若是多次使用alarm函數(shù),則鬧鐘時間被刷新。 .*練習(xí) (1)main函數(shù)中設(shè)置2s定時器,然后注冊SIGALRM信號的處理函數(shù),處理函數(shù)中打印當(dāng)前時間到屏幕上
(2)我們現(xiàn)實中經(jīng)常有這樣的需求,需要每隔2s執(zhí)行某個函數(shù),這樣怎么處理呢? 答案:在定時器處理函數(shù)里邊,再次刷新鬧鐘alarm(2); 4:信號的等待(pause) int pause(void); 特點(diǎn):掛起一個進(jìn)程,直到進(jìn)程收到一個信號,進(jìn)程會繼續(xù)執(zhí)行 上邊的練習(xí),在while循環(huán)中pause()一下。現(xiàn)象:不加pause()之前printf("n = d\n", ++n);會200ms打印一次,然后1秒打印一次時間,加上pause()之后,現(xiàn)象則是1s打印一次printf("n = d\n", ++n),時間也是1s一次。因為pause()會將進(jìn)程掛起,接收到信號之后會繼續(xù),進(jìn)程1s接收一次ALARM信號,則進(jìn)程會1s會被喚醒一次。
#include <stdio.h>
#include <time.h> #include <signal.h> void handler(int sig) {
time_t tim = time(NULL); 相關(guān)資訊
發(fā)表評論
|
全國咨詢電話:400-611-6270,雙休日及節(jié)假日請致電值班手機(jī):15010390966
在線咨詢: 曹老師QQ(3337544669), 徐老師QQ(1462495461), 劉老師 QQ(3108687497)
企業(yè)培訓(xùn)洽談專線:010-82600901,院校合作洽談專線:010-82600350,在線咨詢:QQ(248856300)
Copyright 2004-2018 華清遠(yuǎn)見教育科技集團(tuán) 版權(quán)所有 ,京ICP備16055225號,京公海網(wǎng)安備11010802025203號