可重入函式(reentrant function)
由於用到了strtok函式,順便搜了一下reentrant,看這篇講的不錯,貼來~~
原帖地址:http://blog.chinaunix.net/u/27708/showart_322733.html
可重入函式這一概念早有接觸,但一直未有系統的理解,最近閱讀《 APUE 》訊號一章時,其中講解很到位,故總結如下。
訊號作為一種軟中斷,能夠被程序給捕獲,因而也就中斷程序的正常執行,轉而去執行訊號處理程式,最後再返回到原程序繼續正常執行。然而,當程序正在執行 malloc() 動態記憶體分配時,訊號產生從而轉入到訊號處理程式,但當訊號處理程式中也用到了 malloc() 函式時,問題就出來了?因為 malloc()
因此,在進行上層應用程式設計過程中我們就必須明確哪些函式是可重入性函式(
reentrant functions
)。可重入性函式通常也一定能夠在訊號處理程式(
signal handler
)中被呼叫。
圖 1 能夠在訊號處理程式中呼叫的可重入性函式(節自《 APUE 》)
accept |
fchmod |
lseek |
sendto |
stat |
access |
fchown |
lstat |
setgid |
symlink |
aio_error |
fcntl |
mkdir |
setpgid |
sysconf |
aio_return |
fdatasync |
mkfifo |
setsid |
tcdrain |
aio_suspend |
fork |
open |
setsockopt |
tcflow |
alarm |
fpathconf |
pathconf |
setuid |
tcflush |
bind |
fstat |
pause |
shutdown |
tcgetattr |
cfgetispeed |
fsync |
pipe |
sigaction |
tcgetpgrp |
cfgetospeed |
ftruncate |
poll |
sigaddset |
tcsendbreak |
cfsetispeed |
getegid |
posix_trace_event |
sigdelset |
tcsetattr |
cfsetospeed |
geteuid |
pselect |
sigemptyset |
tcsetpgrp |
chdir |
getgid |
raise |
sigfillset |
time |
chmod |
getgroups |
read |
sigismember |
timer_getoverrun |
chown |
getpeername |
readlink |
signal |
timer_gettime |
clock_gettime |
getpgrp |
recv |
sigpause |
timer_settime |
close |
getpid |
recvfrom |
sigpending |
times |
connect |
getppid |
recvmsg |
sigprocmask |
umask |
creat |
getsockname |
rename |
sigqueue |
uname |
dup |
getsockopt |
rmdir |
sigset |
unlink |
dup2 |
getuid |
select |
sigsuspend |
utime |
execle |
kill |
sem_post |
sleep |
wait |
execve |
link |
send |
socket |
waitpid |
_Exit & _exit |
listen |
sendmsg |
socketpair |
write |
縱觀上表,我們可以看出,有不少系統呼叫函式並沒有出現,換言之也就是非可重入性函式。函式不可重入的原因主要如下:
(1) 函式使用了 static 靜態資料結構
如: struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
struct passwd *getpwent(void);
以上 3 個函式都是返回一個指向 passwd 結構的指標,而該 passwd 結構通常都是函式中 static 變數,其內容在每次呼叫以上函式時都會被重寫。因此,當程序主程式與訊號處理程式中均呼叫了以上函式時,衝突就產生了。
(2) 函式呼叫了 malloc 和 free 函式,正如文章最開始所提到的;
(3) 函式為標準 I/O 的庫函式,因為大多數的標準 I/O 庫函式的實現都使用了 global 全域性資料結構;
因此,若要寫可重入性函式的做法通常是我們在函式中只修改區域性變數,而不改變全域性變數,或儘量不使用全域性變數、靜態static變數。
事實上,與可重入性函式( reentrant function )對應的還有可重入核心( reentrant kernel ),其區別和聯絡在《深入理解 Linux 核心》上有較詳細的講解。