sleep()—程序暫停若干時間。
模擬實現sleep—mysleep.
首先要了解三個函數:alarm,sigaction,pause
①alarm
通俗來講,alarm是一個鬧鐘,鬧鐘是在你設定的時間後叫醒你,也就是告訴內核在seconds秒之後給當前進程發SIGALRM信號, 該信號的默認處理動作是終止當前進程。這個函數的返回值是0(意味着鬧鐘準時響了) ,因爲某些原因(你醒早了)鬧鐘提前響了,則返回的是剩下的秒數。
②sigaction(信號捕捉函數)
sigaction函數可以讀取和修改與指定信號相關聯的處理動作。調用成功則返回0,出錯則返回- 1。
signo是指定信號的編號。若act指針非空,則根據act修改該信號的處理動作。若oact指針非 空,則通過oact傳出該信號原來的處理動作。act和oact指向sigaction結構體。
③pause
庫函數使調用進程(或線程)睡眠狀態,直到接收到信號,要麼終止,或導致它調用一個信號捕獲函數。
返回值
只有當信號被抓獲和信號捕捉函數返回返回。 pause ()返回-1,並將 errno 設置爲 EINTR 。
代碼實現:
int mysleep(int timeout)
{
struct sigaction act,oact;
act.sa_handler = myhandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM,&act,&oact);
alarm(timeout);
pause();
int ret = alarm(0);
sigaction(SIGALRM,&oact,NULL);
return ret;
}
但是,有漏洞存在 : 註冊SIGALRM信號的處理函數,調用alarm(seconds)設定鬧鐘,內核調度優先級更高的進程取代當前進程執行,並且優先級更高的進程有很多個,每個都要執行很長時間seconds秒鐘之後鬧鐘超時了,內核發送SIGALRM信號給這個進程,處於未決狀態。優先級更高的進程執行完了,內核要調度回這個進程執行。SIGALRM信號遞達,執行處理函數sig_alrm之後再次進入內核。返回這個進程的主控制流程,alarm(seconds)返回,調用pause()掛起等待,可是SIGALRM信號已經處理完了,還在等待
int mysleep(int timeout)
{
struct sigaction act,oact;
sigset_t newmask,oldmask,suspmask;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM,&act,&oact);
sigemptyset(&newmask);
sigaddset(&newmask,SIGALRM);
sigprocmask(SIG_BLOCK,&newmask,&oldmask);
alarm(timeout);
suspmask = oldmask;
sigdelset(&suspmask,SIGALRM);
sigsuspend(&suspmask);
int ret = alarm(0);
sigaction(SIGALRM,&oact,NULL);
sigprocmask(SIG_SETMASK,&oldmask,NULL);
return ret;
}
如有錯誤,感謝指正