目錄
信號
signal函數(shù)
sigaction函數(shù)
用信號來處理僵尸進程
????????在之前我們學(xué)習(xí)了如何處理“僵尸進程”,不過可能也會有疑問:調(diào)用wait和waitpid函數(shù)時我們關(guān)注的始終是在子進程上,那么在父進程上如何實現(xiàn)對子進程的管控呢?為此,我們引入一個概念——信號處理。
信號
????????信號這個概念大家應(yīng)該不陌生,諸如汽車的鳴笛、鬧鐘的響鈴等,這些都是信號,用以告訴人們某件事情的發(fā)生。而在計算機中,同樣有這個概念。在編程上常用信號來對接收到的某些數(shù)據(jù)或者指令做出與之對應(yīng)的響應(yīng)處理。
signal函數(shù)
????????讓我們先來看看<signal.h>庫中的signal函數(shù),其結(jié)構(gòu)如下:
#include <signal.h>
void (*signal (int signo , void (*func)(int))) (int);
// 為了在產(chǎn)生信號時調(diào)用,返回之前注冊的函數(shù)指針。
// 函數(shù)名:signal
// 參數(shù) int signo, void (* func)(int)
// 返回類型:參數(shù)為int型,返回void型函數(shù)指針。
????????signo參數(shù)用來標(biāo)記觸發(fā)的條件,func則為將要調(diào)用的函數(shù)的指針。
????????與參數(shù)所相關(guān)的宏有:
- SIGALRM: 按照已通過調(diào)用alarm函數(shù)注冊的時間為信號點。
- SIGINT: 輸入CTRL+C暫停時為信號點
- SIGCHILD: 子進程終止時為信號點。
補充:
????????alarm函數(shù)的用法及意義如下:
#include <unistd.h> unsigned int alarm (unsigned int seconds); // 返回 以秒為單位 的,距 SIGALRM 信號發(fā)生時所剩余的時間
????????其中 seconds 參數(shù)用以傳入欲設(shè)定的alarm的周期(以s為單位)。若傳遞0至變量中,則意味著將會取消對SIGALRM信號的預(yù)約。同時,若未指定該產(chǎn)生信號所對應(yīng)的處理函數(shù)時,則該進程將被終止。
????????接下來,讓我們來驗證下?SIGALRM 和 SIGINT 兩種觸發(fā)條件下的signal函數(shù)吧。
singal.cpp
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void alarmEvent(int sig) //包括下面的keyEvent,這類函數(shù)被稱為信號處理器(Handler)
{
if (sig == SIGALRM)
{
printf("Time out!\n");
}
alarm(2);
}
void keyEvent(int sig)
{
if (sig == SIGINT)
{
printf("\nCTRL+C pressed\n");
}
}
int main(int argc, char *argv[])
{
//注冊:設(shè)置SIGGALRM為觸發(fā)條件,調(diào)用alarmEvent函數(shù)
signal(SIGALRM, alarmEvent);
//注冊:設(shè)置SIGINT為觸發(fā)條件,調(diào)用keyEvent函數(shù)
signal(SIGINT, keyEvent);
//設(shè)置時鐘周期為2s
alarm(2);
for (size_t i = 0; i < 5; i++)
{
printf("Wait for an event to occur...\n");
sleep(50); //實際操作上無法休眠50s
}
return 0;
}
????????運行結(jié)果:
????????想法得到驗證。
????????Q:為什么程序在實際運行上,沒有感受到在等待事件發(fā)生后的50s延遲呢?
????????A:信號在響應(yīng)時,盡管存在有阻塞態(tài)的進程,但操作系統(tǒng)為了調(diào)用信號處理器,會強制喚醒進行阻塞態(tài)(sleep中的)進程,并且該進程一旦被喚醒,將不會在進入睡眠狀態(tài),因此在操作上,我們并不會感受到會有長達50s的延遲。
sigaction函數(shù)
????????sigaction是對signal函數(shù)的升級,能夠更好地支持不同的操作系統(tǒng)(signal函數(shù)在不同的操作系統(tǒng)中用法和效果可能會不同)。
? ? ? ? 讓我們來看下這個函數(shù)長什么樣:
#include <signal.h>
int sigaction(int signo , const struct sigaction * act , struct sigaction *
oldact) ;
// 成功時退回0,失敗時退回-1。
/* 參數(shù)含義 */
// signo:與signal函數(shù)相同,傳遞信號信息
// act:對應(yīng)第一個參數(shù)的信號處理函數(shù)
// oldact:通過此參數(shù)獲取之前注冊的信號處理函數(shù)指針,不需要時則傳遞0。
????????對于sigaction,它的結(jié)構(gòu)體定義如下:
//結(jié)構(gòu)體做了簡化,實際上其中一些變量用宏和typedef做了進一步封裝
struct sigaction
{
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
????????參數(shù)意義參考以下:?
- sa_handler:用以保存信號處理函數(shù)的指針值
- sa_mask:需要濾除的附加信號集,省略
- sa_flags:特殊標(biāo)記符,省略
- sa_restorer:欲重置的(信號)處理器,省略
????????OK,讓我們編寫一個實例來驗證這個函數(shù)的功能
sigaction.cpp
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void alarmEvent(int sig)
{
if (sig == SIGALRM)
{
printf("Time out!\n");
}
alarm(2);
}
int main(int argc, char *argv[])
{
//聲明sigaction結(jié)構(gòu)體變量act
struct sigaction act;
//將信號處理函數(shù)存入結(jié)構(gòu)體中
act.sa_handler = alarmEvent;
//將sa_mask成員的所有位初始化為0
sigemptyset(&act.sa_mask);
//將sa_flags成員同樣初始化為0
act.sa_flags = 0;
//注冊:觸發(fā)條件為SIGALARM,調(diào)用act信號處理器中的信息
sigaction(SIGALRM, &act, 0);
alarm(2);
for (size_t i = 0; i < 5; i++)
{
printf("Wait for an event to occur...\n");
sleep(50);
}
return 0;
}
????????運行結(jié)果:
? ? ? ? 結(jié)果驗證了之前的想法。
用信號來處理僵尸進程
????????當(dāng)我們掌握signal、sigaction兩個函數(shù)后,便可更好地去管控父、子進程了。在之前我們采用的是wait、waitpid函數(shù)來銷毀僵尸進程。接下來,請大家嘗試一下用信號來消滅“僵尸進程”吧!文章來源:http://www.zghlxwxcb.cn/news/detail-478783.html
? ? ? ? 歡迎大家在評論區(qū)貼出你們的代碼~文章來源地址http://www.zghlxwxcb.cn/news/detail-478783.html
到了這里,關(guān)于【TCP/IP】多進程服務(wù)器的實現(xiàn)(進階) - 信號處理及signal、sigaction函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!