pause函數
調用該函數可以造成進程主動掛起,等待信號喚醒。調用該系統調用的進程將處于阻塞狀態(主動放棄cpu) 直到有信號遞達將其喚醒。
????int pause(void); 返回值:-1 并設置errno為EINTR
返回值:
① 如果信號的默認處理動作是終止進程,則進程終止,pause函數么有機會返回。
② 如果信號的默認處理動作是忽略,進程繼續處于掛起狀態,pause函數不返回。
③ 如果信號的處理動作是捕捉,則【調用完信號處理函數之后,pause返回-1】
? ? ? errno設置為EINTR,表示“被信號中斷”。想想我們還有哪個函數只有出錯返回值。
④ pause收到的信號不能被屏蔽,如果被屏蔽,那么pause就不能被喚醒。
pause可以和alarm一起實現sleep函數功能
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h>void catch_sigalrm(int signo) {; }unsigned int mysleep(unsigned int seconds) {int ret;struct sigaction act, oldact;act.as_handler = catch_sigalrm;sigemptyset(&act.sa_mask); //設置屏蔽字act.sa_flags = 0;ret = sigaction(SIGALRM, &act, &oldact);if(ret == -1){perror("sigaction error");exit(1);}alarm(seconds);ret = pause(); //主動掛起 等信號if(ret == -1 && errno == EINTR){printf("pause sucess");}ret = alarm(0); //重置alarmsigaction(SIGALRM, &oldact, NULL);return ret; }
?
時序競態
設想如下場景:
欲睡覺,定鬧鐘10分鐘,希望10分鐘后鬧鈴將自己喚醒。
正常:定時,睡覺,10分鐘后被鬧鐘喚醒。
異常:鬧鐘定好后,被喚走,外出勞動,20分鐘后勞動結束。回來繼續睡覺計劃,但勞動期間鬧鐘已經響過,不會再將我喚醒。
?
?
解決時序問題
?
可以通過設置屏蔽SIGALRM的方法來控制程序執行邏輯,但無論如何設置,程序都有可能在“解除信號屏蔽”與“掛起等待信號”這個兩個操作間隙失去cpu資源。除非將這兩步驟合并成一個“原子操作”。sigsuspend函數具備這個功能。在對時序要求嚴格的場合下都應該使用sigsuspend替換pause。
?
int sigsuspend(const sigset_t *mask); 掛起等待信號。
?
sigsuspend函數調用期間,進程信號屏蔽字由其參數mask指定。
?
可將某個信號(如SIGALRM)從臨時信號屏蔽字mask中刪除,這樣在調用sigsuspend時將解除對該信號的屏蔽,然后掛起等待,當sigsuspend返回時,進程的信號屏蔽字恢復為原來的值。如果原來對該信號是屏蔽態,sigsuspend函數返回后仍然屏蔽該信號。
修改如下;
?
......//設置阻塞信號集,阻塞SIGALRM信號 sigset_t newmask, oldmask, suspmask;sigemptyset(&newmask);sigaddset(&newmask, SIGALRM);sigprocmask(SIG_BLOCK, &newmask, &oldmask); //信號屏蔽字mask alarm(seconds);//構造一個調用sigsuspend臨時有效 阻塞信號集,在臨時阻塞信號集里解除SIGALRM的阻塞suspmask = oldmask;sigdelset(&suspmask, SIGALRM);//sigsuspend調用期間,采用臨時阻塞信號集suspamask替換原有阻塞信號集,這個信號集中不包含SIGALRM信號,同時掛起等待。當sigsuspend被信號喚醒返回時,恢復原有的阻塞信號集sigsuspend(&suspmask);
......
?