溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

線程安全與可重入函數

發布時間:2020-07-08 00:43:50 來源:網絡 閱讀:576 作者:sunshine225 欄目:安全技術
  1. 定義

    (1).線程安全函數:一般說來,一個函數被稱為線程安全的,當它被多個并發線程反復調用時,它會一直產生正確的結果。


    (2).可重入:程序執行到某個函數foo()時,收到信號,于是暫停目前正在執行的函數,轉到信號處理函數,而這個信號處理函數的執行過程中,又恰恰也會進入到剛剛執行的函數foo(),這樣便發生了所謂的重入。此時如果foo()能夠正確的運行,而且處理完成后,之前暫停的foo()也能夠正確運行,則說明它是可重入的。


    (3).拓展: 

    1).如果一個函數中用到了全局或靜態變量,那么它不是線程安全的,也不是可重入的; 

    2).如果我們對它加以改進,在訪問全局或靜態變量時使用互斥量或信號量等方式加鎖,則可以使它變成線程安全的,但此時它仍然是不可重入的,因為通常加鎖方式是針對不同線程的訪問,而對同一線程可能出現問題; 

    3).如果將函數中的全局或靜態變量去掉,改成函數參數等其他形式,則有可能使函數變成既線程安全,又可重入。


  2. 聯系

    可重入函數是線程安全函數的一個真子集。即可重入函數是線程安全函數,但是反過來,線程安全函數未必是可重入函數。


  3. 區別

    (1)解決問題:

    a.可重入函數要解決的問題是,不在函數內部使用靜態或全局數據,不返回靜態或全局數據,也不調用不可重入函數。

    b.線程安全函數要解決的問題是,多個線程調用函數時訪問資源沖突。


    (2)確保措施

  a.確保線程安全的措施是:線程安全函數不使用共享數據(全局、靜態或堆)或者對共享數據實施同步機制保護。

  b.保障可重入的措施:不共享數據并且不調用不可重入函數。

    (1)不要使用static變量和全局變量,堅持只用局部變量;

    (2)若必須訪問全局變量,利用互斥信號量來保護全局變量;

    (3)獲取得知哪些系統調用是可重入的,在多任務處理程序中都使用安全的系統調用;

    (4)不調用其它任何不可重入的函數;

    (5)謹慎使用堆棧malloc/new。


  (3)轉化

   函數如果使用靜態變量,通過加鎖后可以轉成線程安全函數,但仍然有可能不是可重入的,比如    strtok。strtok是既不可重入的,也不是線程安全的。加鎖的strtok不是可重入的,但線程安全。而    strtok_r既是可重入的,也是線程安全的。

  (4)在信號處理函數被調用

 a. 可重入與線程安全的區別體現在能否在信號處理函數中被調用的問題上,可重入函數在信號處理函數中可以被安全調用,因此同時也是異步信號安全函數;而線程安全函數不保證可以在信號處理函數中被安全調用,如果通過設置信號阻塞集合等方法保證一個非可重入函數不被信號中斷,那么它也是異步信號安全函數。

值得一提的是POSIX 1003.1的Syste m Interface缺省是線程安全的,但不是異步信號安全的。異步信號安全的需要明確表示,比如fork ()和signal()。

 b. 一個非可重入函數通常(盡管不是所有情況下)由它的外部接口和使用方法即可進行判斷。例如:strtok()是非可重入的,因為它在內部存儲了被標記分割的字符串;ctime()函數也是非可重入的,它返回一個指向靜態數據的指針,而該靜態數據在每次調用中都被覆蓋重寫。

 c. 線程安全只與函數的內部實現有關,而不影響函數的外部接口。在C語言中,局部變量是在棧上分配的。因此,任何未使用靜態數據或其他共享資源的函數都是線程安全的。一個線程安全的函數通過加鎖的方式來實現多線程對共享數據的安全訪問。


4. 函數的線程不安全與不可重入的原因

(1)任何線程不安全問題的根源都是“共享數據”。所以,不使用任何共享數據的函數(即:可重入函  數)肯定是線程安全的。

(2)不可重入函數的原因在于:

 a. 已知它們使用靜態數據結構
 b. 它們調用malloc和free.
 因為malloc通常會為所分配的存儲區維護一個鏈接表,而插入執行信號處理函數的時候,進程可能正  在修改此鏈接表。
 c. 它們是標準IO函數.
 因為標準IO庫的很多實現都使用了全局數據結構

(3)線程安全函數不一定是可重入函數,因為即使有線程有共享數據,線程被并發調用的時候也可以使  其結果正確--通過同步操作保證正確性。 

  共享數據可以是:

  1. 函數把返回結果放到一個公共的位置

  2. 由調用者傳入的線程間共享的指針變量或者引用變量

  3. 函數內部本來就會使用的共享靜態變量


4. 補充

(1)常見的不可重入函數有:
printf --------引用全局變量stdout
malloc --------全局內存分配表
free    --------全局內存分配表


(2)不可重入的解決方舉例(printf)

例如,程序正在調用printf輸出,但是在調用printf時,出現了信號,對應的信號處理函數也有printf語句,就會導致兩個printf的輸出混雜在一起。
如果是給printf加鎖的話,同樣是上面的情況就會導致死鎖。對于這種情況,采用的方法一般是在特定的區域屏蔽一定的信號。
屏蔽信號的方法:
1> signal(SIGPIPE, SIG_IGN); //忽略一些信號
2> sigprocmask()
sigprocmask只為單線程定義的
3> pthread_sigmask()
pthread_sigmasks可以在多線程中使用





向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女