(不)可重入函數

連接:https://baike.baidu.com/item/%E5%8F%AF%E9%87%8D%E5%85%A5%E5%87%BD%E6%95%B0/4521100?fr=aladdinhtml

          http://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html#POSIX-Safety-Conceptsnode

  • 可重入函數

     在 實時系統的設計中,常常會出現多個任務調用同一個函數的狀況。若是這個函數不幸被設計成爲不可重入的函數的話,那麼不一樣任務調用這個函數時可能修改其餘任 務調用這個函數的數據,從而致使不可預料的後果。那麼什麼是可重入函數呢?所謂可重入是指一個能夠被多個任務調用的過程,任務在調用時沒必要擔憂數據是否會 出錯。不可重入函數在實時系統設計中被視爲不安全函數。安全

     知足下列條件的函數多數是不可重入的:數據結構

(1)函數體內使用了靜態的數據結構;多線程

(2)函數體內調用了malloc()或者free()函數(malloc內部維護了全局的鏈表用來管理分配的內存,這就是狀態信息,free也同樣),並且malloc函數雖然自己是線程安全的,但系統調用時存在全局的heap lock(堆內存鎖(全局鎖),保證堆內存分配時的一致連續性),信號句柄中使用malloc函數時,有可能會發生在主程序調用malloc時,但還未結束,heap lock還未釋放,被信號所中斷,再次調用malloc函數,就會出現同一線程對heap lock連續兩次鎖,即會出現死鎖 deadlock。);函數

(3)函數體內調用了標準I/O函數ui

  (4)進行了浮點運算.許多的處理器/編譯器中,浮點通常都是不可重入的 (浮點運算大多使用協處理器或者軟件模擬來實現。spa

  把一個不可重入函數變成可重入的惟一方法是用可重入規則來重寫他。其實很簡單,只要遵照了幾條很容易理解的規則,那麼寫出來的函數就是可重入的。   操作系統

 (1)不要使用全局變量。由於別的代碼極可能覆蓋這些變量值。線程

 (2)在和硬件發生交互的時候,切記執行相似disinterrupt()之類的操做,就是關閉硬件中斷。完成交互記得打開中斷,在有些系列上,這叫作「進入/退出核心」或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL來描述。//這是臨界區保護

 (3)不能調用任何不可重入的函數。
  (4)謹慎使用堆棧。最好先在使用前先OS_ENTER_KERNAL。

  • 與線程安全的關係

可重入與 線程安全兩個概念都關係到函數處理資源的方式。可是,他們有重大區別:
  • 可重入概念會影響函數的外部接口,而線程安全只關心函數的實現。
    • 大多數狀況下,要將不可重入函數改成可重入的,須要修改函數接口,使得全部的數據都經過函數的調用者提供。
    • 要將非線程安全的函數改成線程安全的,則只須要修改函數的實現部分。通常經過加入 同步機制以保護共享的資源,使之不會被幾個線程同時訪問。
  • 操做系統背景與CPU調度策略:
    • 可重入是在單線程操做系統背景下,重入的函數或者子程序,按照後進先出的線性序依次執行完畢。
    • 多線程執行的函數或子程序,各個線程的執行時機是由操做系統調度,不可預期的,可是該函數的每一個執行線程都會不時的得到CPU的時間片,不斷向前推動執行進度。
  • 可重入函數未必是線程安全的;線程安全函數未必是可重入的。
    • 例如,一個函數打開某個文件並讀入數據。這個函數是可重入的,由於它的多個實例同時執行不會形成衝突;但它不是線程安全的,由於在它讀入文件時可能有別的線程正在修改該文件,爲了線程安全必須對文件加「同步鎖」。
    • 另外一個例子,函數在它的函數體內部訪問共享資源使用了加鎖、解鎖操做,因此它是線程安全的,可是卻不可重入。由於若該函數一個實例運行到已經執行加鎖但未執行解鎖時被停下來,系統又啓動該函數的另一個實例,則新的實例在加鎖處將轉入等待。若是該函數是一箇中斷處理服務,在中斷處理時又發生新的中斷將致使資源死鎖。fprintf函數就是線程安全但不可重入。

 

  

可重入函數列表:

_exit()、 access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、cfsetospeed ()、chdir()、chmod()、chown()、close()、creat()、dup()、dup2()、execle()、 execve()、fcntl()、fork()、fpathconf ()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、 kill()、link()、lseek()、mkdir()、mkfifo()、 open()、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid ()、setpgid()、setsid()、setuid()、 sigaction()、sigaddset()、sigdelset()、sigemptyset()、sigfillset()、 sigismember()、signal()、sigpending()、sigprocmask()、sigsuspend()、sleep()、 stat()、sysconf()、tcdrain()、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、 tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、 umask()、uname()、unlink()、utime()、wait()、waitpid()、write()。

相關文章
相關標籤/搜索