Linux 信号之mysleep
一、????用alarm和pause實(shí)現(xiàn)sleep(3)函數(shù),稱為mysleep。
?
1. main函數(shù)調(diào)用mysleep函數(shù),后者調(diào)用sigaction注冊(cè)了SIGALRM信號(hào)的處理函數(shù)sig_alrm。
2. 調(diào)用alarm(seconds)設(shè)定鬧鐘。
3. 調(diào)用pause等待,內(nèi)核切換到別的進(jìn)程運(yùn)行。
4. seconds秒之后,鬧鐘超時(shí),內(nèi)核發(fā)SIGALRM給這個(gè)進(jìn)程。
5. 從內(nèi)核態(tài)返回這個(gè)進(jìn)程的用戶態(tài)之前處理未決信號(hào),發(fā)現(xiàn)有SIGALRM信號(hào),其處理函數(shù) 是sig_alrm。
6. 切換到用戶態(tài)執(zhí)行sig_alrm函數(shù),進(jìn)入sig_alrm函數(shù)時(shí)SIGALRM信號(hào)被自動(dòng)屏蔽, 從sig_alrm函數(shù)返回時(shí)SIGALRM信號(hào)自動(dòng)解除屏蔽。然后自動(dòng)執(zhí)行系統(tǒng)調(diào)用sigreturn再次進(jìn)入 內(nèi)核,再返回用戶態(tài)繼續(xù)執(zhí)行進(jìn)程的主控制流程(main函數(shù)調(diào)用的mysleep函數(shù))。
7. pause函數(shù)返回-1,然后調(diào)用alarm(0)取消鬧鐘,調(diào)用sigaction恢復(fù)SIGALRM信號(hào)以前的處理 動(dòng)作。
????????????? #include <stdio.h>
#include <signal.h>
#include <unistd.h>
?
void sig_alarm(int signo)//信號(hào)處理函數(shù)
{}
?
int mysleep(int seconds)
{
structsigaction act, oact;
act.sa_handler= sig_alarm;
sigemptyset(&act.sa_mask);
act.sa_flags= 0;
sigaction(SIGALRM,&act, &oact);//注冊(cè)信號(hào)處理函數(shù)
?
alarm(seconds);//設(shè)置鬧鐘
pause();
int_time = alarm(0);//清空鬧鐘
sigaction(SIGALRM,&oact,NULL);//恢復(fù)默認(rèn)信號(hào)處理動(dòng)作
return_time;
}
?
int main()
{
while(1)
{
??????? printf("iam sleeping !\n");
??????? mysleep(3);
?
}
return0;
}
運(yùn)行結(jié)果:每3秒打印一條語(yǔ)句
缺陷:在鬧鐘設(shè)置之后,響應(yīng)之前被切出去,再過(guò)3秒再切回來(lái),就將永遠(yuǎn)收不到信號(hào),進(jìn)程將被永遠(yuǎn)掛起。
思考問(wèn)題:
1、???????????信號(hào)處理函數(shù)sig_alrm什么都沒(méi)?干,為什么還要注冊(cè)它作為SIGALRM的處理函數(shù)?不注冊(cè)信號(hào)處 理函數(shù)可以嗎?
信號(hào)處理機(jī)制:
用函數(shù)signal注冊(cè)一個(gè)信號(hào)捕捉函數(shù)。原型為:
#include?
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
?
signal 的第1個(gè)參數(shù)signum表示要捕捉的信號(hào),第2個(gè)參數(shù)是個(gè)函數(shù)指針,表示要對(duì)該信號(hào)進(jìn)行捕捉的函數(shù),該參數(shù)也可以是SIG_DEF(表示交由系統(tǒng)缺省處理,相當(dāng)于白注冊(cè)了)或SIG_IGN(表示忽略掉該信號(hào)而不做任何處理)。signal如果調(diào)用成功,返回以前該信號(hào)的處理函數(shù)的地址,否則返回 SIG_ERR。
sighandler_t是信號(hào)捕捉函數(shù),由signal函數(shù)注冊(cè),注冊(cè)以后,在整個(gè)進(jìn)程運(yùn)行過(guò)程中均有效,并且對(duì)不同的信號(hào)可以注冊(cè)同一個(gè)信號(hào)捕捉函數(shù)。該函數(shù)只有一個(gè)參數(shù),表示信號(hào)值。
示例:
1)、? 捕捉終端CTRL+c產(chǎn)生的SIGINT信號(hào):
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
?
void SignHandler(int iSignNo)
{
??? printf("Capture sign no:%d/n",iSignNo);?
}
?
int main()
{
??? signal(SIGINT,SignHandler);?
??? while(true)?
??????? sleep(1);?
??? return 0;?
}
該程序運(yùn)行起來(lái)以后,通過(guò)按CTRL+c將不再終止程序的運(yùn)行。應(yīng)為CTRL+c產(chǎn)生的SIGINT信號(hào)已經(jīng)由進(jìn)程中注冊(cè)的SignHandler函數(shù)捕捉了。該程序可以通過(guò)Ctrl+/終止,因?yàn)榻M合鍵Ctrl+/能夠產(chǎn)生SIGQUIT信號(hào),而該信號(hào)的捕捉函數(shù)尚未在程序中注冊(cè)。
?
2)、? 忽略掉終端CTRL+c產(chǎn)生的SIGINT信號(hào):
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
?
int main()
{
??? signal(SIGINT,SIG_IGN);?
??? while(true)?
??????? sleep(1);?
??? return 0;?
}
該程序運(yùn)行起來(lái)以后,將CTRL+C產(chǎn)生的SIGINT信號(hào)忽略掉了,所以CTRL+C將不再能是該進(jìn)程終止,要終止該進(jìn)程,可以向進(jìn)程發(fā)送SIGQUIT信號(hào),即組合鍵CTRL+/
?
3)、? 接受信號(hào)的默認(rèn)處理,接受默認(rèn)處理就相當(dāng)于沒(méi)有寫信號(hào)處理程序:
?
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
?
int main()
{
??? signal(SIGINT,DEF);?
??? while(true)?
??????? sleep(1);?
??? return 0;?
}
2、為什么在mysleep函數(shù)返回前要恢復(fù)SIGALRM信號(hào)原來(lái)的sigaction?
?
??? ??? 當(dāng)某個(gè)信號(hào)的處理函數(shù)被調(diào)用時(shí),內(nèi)核自動(dòng)將當(dāng)前信號(hào)加入進(jìn)程的信號(hào)屏蔽字,當(dāng)信號(hào)處理函數(shù)返回時(shí)自動(dòng)恢復(fù)原來(lái)的信號(hào)屏蔽字,這樣就保證了在處理某個(gè)信號(hào)時(shí),如果這種信號(hào)再次產(chǎn)生,那么它會(huì)被阻塞到當(dāng)前處理結(jié)束為止。
?
?
?
二、? sigsuspend實(shí)現(xiàn)mysleep 函數(shù):
?
對(duì)于第一個(gè)版本的mysleep , 出現(xiàn)這個(gè)問(wèn)題的根本原因是系統(tǒng)運(yùn)行的時(shí)序(Timing)并不像我寫程序時(shí)所設(shè)想的那樣。雖然alarm(nsecs)緊接著的下?行就是pause(),但是?無(wú)法保證pause()?一定會(huì)在調(diào)用alarm(nsecs)之后的nsecs秒之內(nèi)被調(diào)?用。由于異步事件在任何時(shí)候都有可能發(fā)?生(這?的異步事件指出現(xiàn)更高優(yōu) 先級(jí)的進(jìn)程),如果我們寫程序時(shí)考慮不周密,就可能由于時(shí)序問(wèn)題而導(dǎo)致錯(cuò)誤,這叫做競(jìng)態(tài)條件 (Race Condition)。
?
sigsuspend包含了pause的掛起等待功能,同時(shí)解決了競(jìng)態(tài)條件的問(wèn)題,在對(duì)時(shí)序要求嚴(yán)格的場(chǎng)合下都應(yīng)該調(diào)?用sigsuspend而不是pause。
?
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
?
和pause?一樣,sigsuspend沒(méi)有成功返回值,只有執(zhí)?行了?一個(gè)信號(hào)處理函數(shù)之后sigsuspend才返回,返回值為-1,errno設(shè)置為EINTR。
?
調(diào)?用sigsuspend時(shí),進(jìn)程的信號(hào)屏蔽字由sigmask參數(shù)指定,可以通過(guò)指定sigmask來(lái)臨時(shí)解除 對(duì)某 個(gè)信號(hào)的屏蔽,然后掛起等待,當(dāng)sigsuspend返回時(shí),進(jìn)程的信號(hào)屏蔽字恢復(fù)為原來(lái)的值,如果原來(lái)對(duì)該信號(hào)是屏蔽的,從sigsuspend返回后仍然是屏蔽的。
?
以下?用sigsuspend重新實(shí)現(xiàn)mysleep函數(shù):
1、?????????屏蔽SIGALARM信號(hào)
2、?????????alarm(seconds);
3、?????????解除對(duì)SIGALARM信號(hào)的屏蔽。
4、?????????掛起等待pause();
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
?
void sig_alarm(int signo)
{}
?
int mysleep(int seconds)
{
?????? structsigaction act, oact;
?????? sigset_tnewmask, oldmask, suspmask;
//設(shè)置信號(hào)處理函數(shù),保存以前的信息
?????? unsignedint unslept;
?????? act.sa_handler= sig_alarm;
?????? sigemptyset(&act.sa_mask);
?????? act.sa_flags= 0;
?????? sigaction(SIGALRM,&act, &oact);
?? //阻塞信號(hào),保存當(dāng)前的信號(hào)屏蔽字
?????? sigemptyset(&newmask);
?????? sigaddset(&newmask,SIGALRM);
?????? sigprocmask(SIG_BLOCK,&newmask, &oldmask);
?????? //屏蔽SIGALRM
?????? alarm(seconds);
?????? suspmask= oldmask;
?????? sigdelset(&suspmask,SIGALRM);
?????? sigsuspend(&suspmask);
//解除屏蔽,掛起等待//SIGALRM信號(hào)遞達(dá)后,sigsuspend返回,自動(dòng)恢復(fù)原來(lái)的屏蔽字,自動(dòng)恢復(fù)原來(lái)的屏蔽字,即再次屏蔽SIGMASK
int _time =alarm(0);
?????? sigaction(SIGALRM,&act, NULL);
//恢復(fù)默認(rèn)的信號(hào)處理動(dòng)作
?????? sigprocmask(SIG_SETMASK,&oldmask, NULL);
???? ?//重置信號(hào)屏蔽字,再次解除對(duì)SIGALRM的屏蔽。
?????? return_time;
}
?
int main()
{
?????? while(1)
?????? {
????????????? mysleep(5);
????????????? printf("iam sleeping !\n");
?????? }
?????? return0;
}
?
運(yùn)行結(jié)果:每隔五秒響應(yīng)動(dòng)作,打印語(yǔ)句。
總結(jié)
以上是生活随笔為你收集整理的Linux 信号之mysleep的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 兄弟7895dw粉盒清零_兄弟broth
- 下一篇: 炸裂!跑P站上教微积分,年入170w..