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