linux 下實現(xiàn)sleep詳解及簡單實例
sleep:
普通版本
1、基本設(shè)計思路:
1>注冊SIGALRM信號的處理函數(shù);
2>調(diào)用alarm(nsecs)設(shè)定鬧鐘;
3>調(diào)⽤pause等待,內(nèi)核切換到別的進(jìn)程運行;
4>nsecs秒之后,鬧鐘超時,內(nèi)核發(fā)SIGALRM給這個進(jìn)程 ;
5>從內(nèi)核態(tài)返回這個進(jìn)程的⽤戶態(tài)之前處理未決信號,發(fā)現(xiàn)有SIGALRM信號,其處理函數(shù)是sig_alrm;
6> 切換到用戶態(tài)執(zhí)行sig_alrm函數(shù),進(jìn)⼊sig_alrm函數(shù)時SIGALRM信號被⾃動屏蔽,從sig_alrm函數(shù)返回SIGALRM信 號⾃動解除屏蔽。然后⾃動執(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信號以前的處理動作。
2、實現(xiàn)代碼
#includestdio.h>
#includesignal.h>
void handler(int signo)
{}
int mysleep(int timeout)
{
struct sigaction act,oact;
act.sa_handler = handler;
act.sa_flags = 0;
sigemptyset(act.sa_mask);
sigaction(SIGALRM,act,oact);
alarm(timeout);
pause();
int ret = alarm(0);
sigaction(SIGALRM,oact,NULL);
return ret;
}
int main()
{
while(1)
{
printf("using musleep!\n");
mysleep(3);
}
return 0;
}
相關(guān)函數(shù)分析:
#include unistd.h>
int pause(void);
pause函數(shù)使調(diào)⽤進(jìn)程掛起直到有信號遞達(dá)。如果信號的處理動作是終⽌進(jìn)程,則進(jìn)程終⽌,pause函數(shù)沒有機會返回;如果信號的處理動作是忽略,則進(jìn)程繼續(xù)處于掛起狀態(tài),pause不返回;如果信號的處理動作是捕捉,則調(diào)⽤了信號處理函數(shù)之后pause返回-1,errno設(shè)置為EINTR, 所以pause只有出錯的返回值 。
sigaction函數(shù)
#include signal.h>
int sigaction(int signo, const struct sigaction *act, struct
sigaction *oact);
sigaction函數(shù)可以讀取和修改與指定信號相關(guān)聯(lián)的處理動作。調(diào)⽤成功則返回0,出錯則返回- 1。 signo是指定信號的編號。若act指針⾮空,則根據(jù)act修改該信號的處理動作。若oact指針非 空,則通過oact傳出該信號原來的處理動作。
int sigemptyset(sigset_t *set);
函數(shù)sigemptyset初始化set所指向的信號集,使其中所有信號的對應(yīng)bit清零,表⽰該信號集不包含 任何有效信號。
二、優(yōu)化版本
所需函數(shù)分析
#include signal.h>
int sigsuspend(const sigset_t *sigmask);
sigsuspend沒有成功返回值,只有執(zhí)⾏了⼀個信號處理函數(shù)之后sigsuspend才返回,返回值為-1,errno設(shè)置為EINTR。調(diào)⽤sigsuspend時,進(jìn)程的信號屏蔽字由sigmask參數(shù)指定,可以通過指定sigmask來臨時解除對某 個信號的屏蔽,然后掛起等待,當(dāng)sigsuspend返回時,進(jìn)程的信號屏蔽字恢復(fù)為原來的值,如果原來對該信號是屏蔽的,sigsuspend返回后仍然是屏蔽的。
sigsuspend函數(shù)與pause函數(shù):都可以將程序掛起,但是sigsuspend函數(shù)可以實現(xiàn)對信號屏蔽字的解除與掛起。
sigprocmask
調(diào)⽤函數(shù)sigprocmask可以讀取或更改進(jìn)程的信號屏蔽字(阻塞信號集)。
#include signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
如果oset是⾮空指針,則讀取進(jìn)程的當(dāng)前信號屏蔽字通過oset參數(shù)傳出。如果set是⾮空指針,則 更改進(jìn)程的信號屏蔽字,參數(shù)how指⽰如何更改。如果oset和set都是⾮空指針,則先將原來的信號 屏蔽字備份到oset⾥,然后根據(jù)set和how參數(shù)更改信號屏蔽字。
how的選項意義

如果調(diào)⽤sigprocmask解除了對當(dāng)前若⼲個未決信號的阻塞,則在sigprocmask返回前,⾄少將其中⼀個信號遞達(dá)。
代碼實現(xiàn):
#includestdio.h>
#includesignal.h>
void handler(int signo)
{}
int mysleep(int timout)
{
struct sigaction act,oact;
sigset_t newmask,oldmask,suspmask;
act.sa_handler = handler;
act.sa_flags = 0;
sigemptyset(act.sa_mask);
sigaction(SIGALRM,act,oact);
sigemptyset(newmask);
sigaddset(newmask,SIGALRM);
sigprocmask(SIG_BLOCK,newmask,oldmask);
alarm(timout);
suspmask = oldmask;
sigdelset(suspmask,SIGALRM);
sigsuspend(suspmask);
int unslept = alarm(0);
sigaction(SIGALRM,oact,NULL);
sigprocmask(SIG_SETMASK,oldmask,NULL);
return(unslept);
}
int main()
{
while(1)
{
printf("using musleep!\n");
mysleep(3);
}
return 0;
}
優(yōu)化版本解決了普通版本存在的競態(tài)問題。我們重新審視一下普通版本的時序問題。
1、設(shè)置SIGALRM信號的處理函數(shù);
2、調(diào)用alarm()函數(shù)設(shè)置鬧鐘;
3、內(nèi)核選取更高優(yōu)先級的進(jìn)程來取代當(dāng)前進(jìn)程,并且這樣的進(jìn)程很多,同時執(zhí)行時間又很長;
4、鬧鐘超時了,內(nèi)核發(fā)送SIGALRM信號給該進(jìn)程,并且處于未決狀態(tài);
5、優(yōu)先級更高的進(jìn)程結(jié)束后,內(nèi)核要調(diào)度回這個進(jìn)程執(zhí)⾏。 SIGALRM信號遞達(dá),執(zhí)⾏處理函 數(shù)sig_alrm之后再次進(jìn)⼊內(nèi)核。
6、返回這個進(jìn)程的主控制流程,alarm(nsecs)返回,調(diào)⽤pause()掛起等待。
7、可是現(xiàn)在SIGALRM信號已經(jīng)被處理,進(jìn)程會導(dǎo)致錯誤。
在一個進(jìn)程運行過程中,因為由于異步,所以可能被其他優(yōu)先級更高的進(jìn)程,由于時序問題而引發(fā)的錯誤問題。這樣的問題稱為競態(tài)問題。
優(yōu)化版本中,先將設(shè)置SIGALRM信號的處理函數(shù),然后將SIGALRM信號進(jìn)行屏蔽,然后調(diào)用alarm()函數(shù)設(shè)置鬧鐘,然后調(diào)用sigprocmask()函數(shù)對SIGALRM信號解除屏蔽然后掛起等待,這樣就解決了競態(tài)問題。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
您可能感興趣的文章:- Linux模擬實現(xiàn)sleep函數(shù)