編寫乙個mysleep函式

2021-08-03 02:55:09 字數 3222 閱讀 9795

在這之前,我寫了有關於訊號的相關知識,我在裡面講到了兩個函式,分別是alarm和pause兩個函式,這兩個函式是幹嘛的呢,我在這裡再介紹一遍。

呼叫alarm

函式可以設定乙個鬧鐘

,也就是告訴核心在

seconds

秒之後給當前程序發

sigalrm

訊號,

該訊號的預設處理動作是終止當前程序。

這個函式的返回值是

0或者是以前設定的鬧鐘時間還餘下的秒數

pause函式使呼叫程序掛起直到有訊號遞達。如果訊號的處理動作是終止程序,則程序終止,pause函式沒有機會返回;如果訊號的處理動作是忽略,則程序繼續處於掛起狀態,pause不返回;如果訊號的處理動作是捕捉,則呼叫了訊號處理函式之後pause返回-1,errno設定為eintr, 所以pause只有出錯的返回值。錯誤碼eintr表示「被訊號中斷」。

下面我用alarm和pause實現sleep()函式:

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訊號以前的處理動作。

在看了上面的**之後,我們可以去執行一下

我們看到這裡的效果和實現乙個sleep的效果是一致的。

但其實,這裡的程式是帶有一點小bug的,你可以看出來嗎?

給點提示,bug在第26行設定鬧鐘這裡。

我們現在設想一下,給出這樣的操作時序:

1、註冊sigalrm訊號處理函式

2、呼叫alarm(seconds)函式設定鬧鐘

3、核心排程優先順序更高的程序替代當前程序執行,並且優先順序更高的程序有很多個,每個都需要執行很長的時間

4、seconds秒之後鬧鐘超時了,核心傳送sigalrm訊號給這個程序,處於未決狀態

5、優先順序更高的程序執行完了,核心要排程這個程序執行。sigalrm訊號遞達,執行處理函式sig_alrm()之後再次進入核心

6、返回這個程序的主控制流程,alarm(seconds)返回,呼叫pause()掛起等待

7、但是,此時sigalrm訊號已經被處理完了,那還需要等待什麼呢?

出現這個問題的根本原因是系統執行的時序(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。

下面我就用sigsuspend去重新實現一下mysleep函式

如果在呼叫mysleep函式時sigalrm訊號沒有遮蔽:

1. 呼叫sigprocmask(sig_block, &mask, &omask);時遮蔽sigalrm。

2. 呼叫sigsuspend(&suspmask);時解除對sigalrm的遮蔽,然後掛起等待待。

3. sigalrm遞達後suspend返回,自動恢復原來的遮蔽字,也就是再次遮蔽sigalrm。

4. 呼叫sigprocmask(sig_setmask, &omask, null);時再次解除對sigalrm的遮蔽。

然後我們執行一下程式:

編寫函式diamond列印乙個菱形

編寫函式diamond列印乙個菱形 linux 上c語言程式設計 此為c一站式學習書上的一道習題,想了很久終於有解,將方法貼出來,方便以後學習時用 總結 1。對於列印一類題目始終應該考慮字元的輸出順序,如此題中應該先輸出空格再輸出定義的字元,所以寫時先考慮空格輸出,再考慮特殊字元輸出。2.找出數字規...

編寫乙個名為 collatz 的函式

編寫乙個名為 collatz 的函式,它有乙個名為 number 的引數。如果引數是偶數,那麼 collatz 就列印出 number 2,並返回該值。如果 number 是奇數,collatz 就打 印並返回 3 number 1。然後編寫乙個程式,讓使用者輸入乙個整數,並不斷對這個數呼叫 col...

編寫乙個makefile

什麼是makefile?對於大多數的windows程式設計師來講,makefile可能不是那麼重要,因為windows的ide都為程式設計師做好了這個工作。但是在linux下程式設計,會不會寫makefile,從側面上說明乙個人是否具備完成大型工程的能力。makefile的作用 makefile是用...