訊號的原理和利用--實現sleep函式
訊號就是當一個程序收到一個訊號,程序就要對個訊號作出相關處理,比如我們常用的 kill-9 PID ,這條命令就是向某個程序傳送9號訊號,以終止這個程序,那麼具體到底怎麼實現程序的終止的呢?讓我們一起來看一下。
我們要明白以下三點:
1.程序可以對個訊號作出相關處理的前提是程序可以識別這個訊號。
2.程序收到一個訊號的時候不一定會立即處理,可能會先儲存這個訊號的資訊(在PCB中),所以當一個程序的PCB中有相關訊號的資訊,也就是PCB中訊號相關欄位增加了資料時,我們說操作給這個程序傳送了一個訊號,所以,收到一個訊號的本質是PCB中訊號的有關欄位發生改變。
3.PCB中用一個整數,32個位元位,用位元位的序號表示收到訊號的序號,用該位元位的內容表示是否收到該訊號,這個用以表示是否收到訊號的表叫做pending表。
舉個例子,前臺執行某個程式的時候,我們在鍵盤輸入 Ctrl+C,就會終止這個程序,這是因為前臺程序捕捉了這個有鍵盤傳送過來的訊號,並執行了相應的處理程式,那麼什麼又是訊號捕捉呢?
如果訊號的處理動作是使用者自定義函式,在訊號遞達時就呼叫這個函式,這稱為捕捉訊號。由於訊號處理函式的程式碼是在使用者空間的,處理過程比較複雜,舉例如下:
1. 使用者程式註冊了SIGQUIT訊號的處理函式sighandler。
2. 當前正在執行main函式,這時發生中斷或異常切換到核心態。
3. 在中斷處理完畢後要返回使用者態的main函式之前檢查到有信SIGQUIT遞達。
4. 核心決定返回使用者態後不是恢復main函式的上下文繼續執行,而是執行sighandler函式,sighandler和main函式使用不同的堆疊空間,它們之間不存在呼叫和被呼叫的關係,
是 兩個獨立的控制流程。
5. sighandler函式返回後自動執行特殊的系統呼叫sigreturn再次進入核心態。
6. 如果沒有新的訊號要遞達,這次再返回使用者態就是恢復main函式的上下文繼續執行了。
用影象描述就是:
所以我們可以看到一次訊號捕捉有四次狀態切換:使用者->核心->使用者->核心->使用者,這裡我們需要注意的點是:上述的第四點,在此強點一下,執行處理訊號的函式的時候,並不是回到main函式的堆疊中執行,而是是直接到這個程式的程式碼處執行,並且是以使用者態的許可權。
簡述一下我們要利用系統呼叫介面和訊號實現mysleep:
下面這個系統呼叫的作用是自定義預設的訊號處理函式,
這個系統呼叫的作用是seconds秒之後會 call作業系統:
這個系統呼叫的作用是呼叫會後當前程序會被掛起,直到接收到一個訊號,這個程序才會被執行。
所以我們就有了以下的程式碼:
簡單的解釋一下:
- handler定義成一個空函式的原因是因為,mysleep就是為了讓程式休眠,所以定義成一個空函式,什麼也不做。
- 第二次sigaction(SIGALRAM,&act,&NULL)的作用是為了恢復原來的鬧鐘相關狀態資訊。
執行結果:
這裡我們看到每間隔3秒鐘列印一句:mysleep……
但是,這個版本的mysleep存在一個bug,那就是,競態問題。