可重入 不可重入

2021-07-08 13:40:26 字數 2796 閱讀 3515

在任務執行期間捕捉到訊號並對其進行處理時,程序正在執行的指令序列就被訊號處理程式臨時中斷。如果從訊號處理程式返回,則繼續執行程序斷點處的正常指令序列,從重新恢復到斷點重新執行的過程中,函式所依賴的環境沒有發生改變,就說這個函式是可重入的,反之就是不可重入的。 

眾所周知,在程序中斷期間,系統會儲存和恢復程序的上下文,然而恢復的上下文僅限於返回位址,cpu暫存器等之類的少量上下文,而函式內部使用的諸如全域性或靜態變數,buffer等並不在保護之列,所以如果這些值在函式被中斷期間發生了改變,那麼當函式回到斷點繼續執行時,其結果就不可預料了。打個比方,比如malloc,將如乙個程序此時正在執行malloc分配堆空間,此時程式捕捉到訊號發生中斷,執行訊號處理程式中恰好也有乙個malloc,這樣就會對程序的環境造成破壞,因為malloc通常為它所分配的儲存區維護乙個鏈結表,插入執行訊號處理函式時,程序可能正在對這張表進行操作,而訊號處理函式的呼叫剛好覆蓋了程序的操作,造成錯誤。

滿足下面條件之一的多數是不可重入函式:

(1)使用了靜態資料結構;

(2)呼叫了malloc或free;

(3)呼叫了標準i/o函式;標準io庫很多實現都以不可重入的方式使用全域性資料結構。

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

1) 訊號處理程式a內外都呼叫了同乙個不可重入函式b;b在執行期間被訊號打斷,進入a (a中呼叫了b),完事之後返回b被中斷點繼續執行,這時b函式的環境可能改變,其結果就不可預料了。

2) 多執行緒共享程序內部的資源,如果兩個執行緒a,b呼叫同乙個不可重入函式f,a執行緒進入f後,執行緒排程,切換到b,b也執行了f,那麼當再次切換到執行緒a時,其呼叫f的結果也是不可預料的。

在訊號處理程式中即使呼叫可重入函式也有問題要注意。作為乙個通用的規則,當在訊號處理程式中呼叫可重入函式時,應當在其前儲存errno,並在其後恢復errno。(因為每個執行緒只有乙個errno變數,訊號處理函式可能會修改其值,要了解經常**捉到的訊號是sigchld,其訊號處理程式通常要呼叫一種wait函式,而各種wait函式都能改變errno。)

可重入函式列表:

_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()。

書上關於訊號處理程式中呼叫不可重入函式的例子:

#include

#include

#include

static void func(int signo)

signal(sigalrm,func);

alarm(1);

}int main(int argc, char** argv)

}return 0;

}signal了乙個sigalrm,而後設定乙個定時器,在for函式執行期間的某個時刻,也許就是在getpwnam函式執行期間,相應訊號發生中斷,進入訊號處理函式func,在執行func期間又收到alarm發出的訊號,getpwnam可能再次中斷,這樣就很容易發生不可預料的問題。

不可重入函式不可以在它還沒有返回就再次被呼叫。例如printf,malloc,free等都是不可重入函式。因為中斷可能在任何時候發生,例如在printf執行過程中,因此不能在中斷處理函式裡呼叫printf,否則printf將會被重入。 

函式不可重入大多數是因為在函式中引用了全域性變數。例如,printf會引用全域性變數stdout,malloc,free會引用全域性的記憶體分配表。

個人理解:如果中斷發生的時候,當執行到printf的時候,假設發生了中斷巢狀,而此時stdout資源被占用,所以第二個中斷printf等待第乙個中斷的stdout資源釋放,第乙個中斷等待第二個中斷返回,造成了死鎖,不知這樣理解對不對。

不可重入函式指的是該函式在被呼叫還沒有結束以前,再次被呼叫可能會產生錯誤。可重入函式不存在這樣的問題。

不可重入函式在實現時候通常使用了全域性的資源,在多執行緒的環境下,如果沒有很好的處理資料保護和互斥訪問,就會發生錯誤。

常見的不可重入函式有:

printf --------引用全域性變數stdout

malloc --------全域性記憶體分配表

free    --------全域性記憶體分配表

在unix裡面通常都有加上_r字尾的同名可重入函式版本。如果實在沒有,不妨在可預見的發生錯誤的地方嘗試加上保護鎖同步機制等等。

可重入與不可重入

這種情況出現在多工系統當中,在任務執行期間捕捉到訊號並對其進行處理時,程序正在執行的指令序列就被訊號處理程式臨時中斷。如果從訊號處理程式返回,則繼續執行程序斷點處的正常指令序列,從重新恢復到斷點重新執行的過程中,函式所依賴的環境沒有發生改變,就說這個函式是可重入的,反之就是不可重入的。眾所周知,在程...

可重入和不可重入

重入一般可以理解為乙個函式在同時多次呼叫,例如作業系統在程序排程過程中,或者微控制器 處理器等的中斷的時候會發生重入的現象。一般浮點運算都是由專門的硬體來完成,舉個例子假設有個硬體暫存器名字叫做float,用來計算和存放浮點數的中間運算結果 假設有這麼個函式 void fun 假如第一次執行,有個對...

可重入和不可重入

重入一般可以理解為乙個函式在同時多次呼叫,例如作業系統在程序排程過程中,或者微控制器 處理器等的中斷的時候會發生重入的現象。一般浮點運算都是由專門的硬體來完成,舉個例子假設有個硬體暫存器名字叫做float,用來計算和存放浮點數的中間運算結果 假設有這麼個函式 void fun 假如第一次執行,有個對...