1. 程式人生 > >訊號之可重入函式、競態條件

訊號之可重入函式、競態條件

一、可重入函式

1.基本概念

 當捕捉到訊號時,不論程序的主控制流程當前執行到哪兒,都會先跳到訊號處理函式中執行,從 訊號處理函式返回後再繼續執行主控制流程。訊號處理函式是一個單獨的控制流程,因為它 和主控制流程是非同步的,二者不存在調⽤用和被調⽤用的關係,並且使用不同的堆疊空間。引入了 訊號處理函式使得一個程序具有多個控制流程,如果這些控制流程訪問相同的全域性資源(全域性 變數、硬體資源等),就有可能出現衝突。      函式被不同的控制流程呼叫,有可能在第一次呼叫還沒返回時就再次 進入該函 數,這稱為重入,insert函式訪問一個全域性連結串列,有可能因為重入而造成錯亂,像這樣 的函式稱為 不可重入函式,反之,如果一個函式只訪問自己的區域性變數或引數,則稱為可重入 (Reentrant) 函式。 對於不可重入函式:      1.呼叫了malloc或free      2.呼叫了標準I/O庫函式 2.可重入函式與執行緒安全
執行緒安全定義:如果程式碼所在程序中有多個執行緒同時執行,而這些執行緒可能同時執行這段程式碼,若每次執行和單執行緒執行時結果一樣,且其他變數與預期結果也是一樣的,則是執行緒安全的。 執行緒不安全的函式:
  • 不保護共享變數的函式;
  • 函式狀態隨著呼叫改變的函式;
  • 返回指向靜態變數指標的函式;
  • 呼叫執行緒不安全函式的函式
對於可重入函式與執行緒安全對比: 可重入和執行緒安全(Thread-Safe)是兩個不同的概念:可重入函式一定是執行緒安全的;執行緒安全的函式可能是重入的,也可能是不重入的;執行緒不安全的函式一定是不可重入的。
可重入函式要解決的問題是,不在函式內部使用靜態或全域性資料,不返回靜態或全域性資料,也不呼叫不可重入函式。

執行緒安全函式要解決的問題是,多個執行緒呼叫函式時訪問資源衝突。

函式如果使用靜態變數,通過加鎖後可以轉成執行緒安全函式,但仍然有可能不是可重入的。 二、競態條件與sigsuspend函式
     在上面自己定義的my_sleep函式中,如果鬧鐘剛設定完畢程序被切出去,作業系統發出鬧鐘訊號後進程才被切進來繼續,這樣pause函式執行則不能接收到alarm訊號      這個問題的根本原因是系統執行的時序問題。雖然alarm緊接著下面就是pause,但是還是無法保證pause函式一定會呼叫alarm設定時間內呼叫。      像這類由於時序問題導致的錯誤,叫做競態條件。      sigsuspend解決了竟態條件的問題,它能夠使“解除訊號遮蔽”和“掛起等待訊號”合併成一個原子操作。所以在對時序要求嚴格的場合下都應該呼叫sigsuspend而不是pause

  調⽤用sigsuspend時,程序的訊號遮蔽字由sigmask引數指定,可以通過指定sigmask來臨時 解除對某 個訊號的遮蔽,然後掛起等待,當sigsuspend返回時,程序的訊號遮蔽字恢復為原 來的值,如果原來對該訊號是遮蔽的,從sigsuspend返回後仍然是遮蔽的。  三、SIGCHILD訊號      子程序在終止時會給父程序傳送SIGCHILD訊號,該訊號的預設處理動作是忽略,父程序可以自定義SIGCHILD函式。子程序在終止時會通知父程序,父程序在訊號處理函式中呼叫wait清理子程序