alarm函式
呼叫alarm函式可以設定乙個鬧鐘,也就是告訴核心在seconds秒之後給當前程序發sigalrm訊號, 該訊號的預設處理動作是終止當前程序。 這個函式的返回值是0或者是以前設定的鬧鐘時間還餘下的秒數。
pause函式
pause函式使呼叫程序掛起直到有訊號遞達。如果訊號的處理動作是終止程序,則程序終止,pause函式沒有機會返回;如果訊號的處理動作是忽略,則程序繼續處於掛起狀態,pause不返回;如果訊號的處理動作是捕捉,則呼叫了訊號處理函式之後pause返回-1,errno設定為eintr, 所以pause只有出錯的返回值。錯誤碼eintr表示「被訊號中斷」。
**實現
1、main函式呼叫mysleep函式,後者呼叫sigaction註冊了sigalrm訊號的處理函式sig_alarm()
2、呼叫alarm(seconds)函式設定鬧鐘
3、呼叫pause等待,核心切換到別的執行緒去執行
4、seconds秒之後,鬧鐘超時,核心發sigalrm訊號給這個程序
5、在核心態返回這個程序的使用者態之前處理未決訊號,發現有sigalrm訊號,其處理函式是sig_alram()
6、切換到使用者態執行sig_alarm()函式,進入sig_alarm()函式時sigalrm訊號自動被遮蔽,從sig_alarm()函式返回時sigalrm訊號自動解除遮蔽。然後自動執行系統呼叫sigreturn再次進入核心,再返回使用者態繼續執行程序的主控制流程(main函式呼叫的mysleep函式)。
7、pause函式返回-1,然後呼叫alarm(0)取消鬧鐘,呼叫sigaction恢復sigalrm訊號以前的處理動作。
結果如下
但其實,前面的mysleep()函式的實現有缺陷。
我們現在設想一下,給出這樣的操作時序:
1、註冊sigalrm訊號處理函式
2、呼叫alarm(seconds)函式設定鬧鐘
3、核心排程優先順序更高的程序替代當前程序執行,並且優先順序更高的程序有很多個,每個都需要執行很長的時間
4、seconds秒之後鬧鐘超時了,核心傳送sigalrm訊號給這個程序,處於未決狀態
5、優先順序更高的程序執行完了,核心要排程這個程序執行。sigalrm訊號遞達,執行處理函式sig_alrm()之後再次進入核心
6、返回這個程序的主控制流程,alarm(seconds)返回,呼叫pause()掛起等待
7、但是,此時sigalrm訊號已經被處理完了,那還需要等待什麼呢?
競態條件和sigsuspend函式
出現這個問題的根本原因是系統執行的時序(timing)並不像我們寫程式時所設想的那樣。雖然alarm(seconds)緊接著的下一行就是pause(),但是無法保證pause()一定會在呼叫alarm(seconds)之後的seconds秒之內被呼叫。由於非同步事件在任何時候都有可能發生(這裡的非同步事件指出現更高優先順序的程序)。如果我們寫程式時考慮不周密,就可能由於時序問題而導致錯誤,這叫做競態條件(race condition)。
那麼在理解這個問題之後,我們應該能夠想出這樣的處理方法->在呼叫pause之前遮蔽sigalrm訊號使它不能提前遞達就好了。
首先看一下下面的方法:
1、遮蔽sigalrm訊號
2、alarm(seconds)
3、解除對sigalrm訊號的遮蔽
4、pause()
但是呢,從解除訊號遮蔽到呼叫pause之間任然存在間隙,sigalrm訊號任然有可能在這個間隙之間遞達。若要消除這個間隙,我們將解除遮蔽移到pause之後可以嘛?
1、遮蔽sigalrm訊號
2、alarm(seconds)
3、pause()
4、解除對sigalrm訊號的遮蔽
這樣就更不行了,還沒有解除遮蔽就呼叫pause,pause根本不可能等到sigalrm訊號。
所以,我們還是想要通過第一種方法去處理這個問題,只要將「解除訊號遮蔽」和「掛起等待訊號」這兩步合併成乙個原子操作就好了。
所以,這裡我再介紹到sigsuspend這個函式,上面的要求正是sigsuspend函式的功能。 sigsuspend包含了pause的掛起等待功能,同時解決了競態條件的問題,在對時序要求嚴格的場合下都應該呼叫sigsuspend而不是pause。
和pause()一樣,sigsuspend()沒有成功返回值,只有執行了乙個訊號處理函式之後sigsuspend()才返回,返回值為-1,errno設定為eintr。
如果在呼叫mysleep函式時sigalrm訊號沒有遮蔽:
1.呼叫sigprocmask(sig_block, &mask, &omask);時遮蔽sigalrm。
2.呼叫sigsuspend(&suspmask);時解除對sigalrm的遮蔽,然後掛起等待待。
3.sigalrm遞達後suspend返回,自動恢復原來的遮蔽字,也就是再次遮蔽sigalrm。
4.呼叫sigprocmask(sig_setmask, &omask, null);時再次解除對sigalrm的遮蔽。
訊號之signal函式
unix系統的訊號機制最簡單的介面是signal函式。signal函式的功能 為指定的訊號安裝乙個新的訊號處理函式。include void signal int signo,void func int int 複雜原型分開看 void signal int signo,void func int ...
訊號之sleep函式
include unsigned int sleep unsigned int seconds 返回值 0或未休眠夠的秒數 此函式使呼叫程序被掛起,直到滿足以下條件之一 1 已經過了seconds所指定的牆上時鐘時間。2 呼叫程序捕捉到乙個訊號並從訊號處理程式返回。如果alarm訊號一樣,由於其他系...
訊號之sigprocmask函式
乙個程序的訊號遮蔽字規定了當前阻塞而不能遞送給該程序的訊號集。呼叫函式sigprocmask可以檢測或更改其訊號遮蔽字,或者在乙個步驟中同時執行這兩個操作。include int sigprocmask int how,const sigset t restrict set,sigset t res...