可重入函式這一概念早有接觸,但一直未有系統的理解,最近閱讀《
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
dupgetsockopt
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
縱觀上表,我們可以看出,有不少系統呼叫函式並沒有出現,換言之也就是非可重入性函式。函式不可重入的原因主要如下:
函式使用了
static
靜態資料結構 如:
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
struct passwd *getpwent(void); 以上
3個函式都是返回乙個指向
passwd
結構的指標,而該
passwd
結構通常都是函式中
static
變數,其內容在每次呼叫以上函式時都會被重寫。因此,當程序主程式與訊號處理程式中均呼叫了以上函式時,衝突就產生了。
函式呼叫了
malloc
和free
函式,正如文章最開始所提到的;
函式為標準
i/o的庫函式,因為大多數的標準
i/o庫函式的實現都使用了
global
全域性資料結構;
因此,若要寫可重入性函式的做法通常是我們在函式中只修改區域性變數,而不改變全域性變數,或盡量不使用全域性變數、靜態static變數。
事實上,與可重入性函式(
reentrant function
)對應的還有可重入核心(
reentrant kernel
),其區別和聯絡在《深入理解
linux
核心》上有較詳細的講解。
可重入核心:所有unix核心都是可重入的(reentrant),這意味著幾個程序可以同時在核心態下執行,當然在單處理器系統上,只有乙個程序在真正的執行,但是許多程序可以在核心態下阻塞,或者等待cpu,或者等待一些i/o操作的完成。
提供可重入的一種方式是編寫函式,這些函式只能修改區域性變數,不能修改全域性資料結構,這樣的函式叫做可重入函式。
但是可重入核心不僅僅侷限於這樣的可重入函式(儘管一些實時核心正是如此實現的),核心可以包含非重入函式,並且利用鎖機制保證一次只有乙個程序執行乙個非重入函式。處於核心態的每個程序只能作用於自己的記憶體空間,不能干預其它的程序。
可重入核心 可重入函式
可重入核心在ulk 深入理解linux核心 中的定義是指若干個程序可以同時在核心態下執行,也就是說多個程序可以在核心態下併發執行核心 在單處理器上,只能實現 微觀上的序列,巨集觀上的並行,即任意時刻,只有乙個進 正執行,其他程序處於阻塞或者等待狀態。這裡的可重入,是指可以多個程序進入核心,並不是重複...
可重入核心
所有的unix核心都是可重入的,這意味著若干個程序可以同時在核心態下執行 提供可重入的一種方式是編寫函式,以編寫這些函式只能修改區域性變數,而不能修改全域性資料結構,這樣的函式叫可重入函式。但是可重入核心不僅僅侷限於這樣的可重入函式。相反,可重入核心可以包含非重入函式,並且利用鎖機制保證一次只有乙個...
可重入函式
在實時系統的設計中,經常會出現多個任務呼叫同乙個函式的情況。如果這個函式不幸被設計成為不可重入的函式的話,那麼不同任務呼叫這個函式時可能修改其他任務呼叫這個函式的資料,從而導致不可預料的後果。那麼什麼是可重入函式呢?所謂可重入函式是指乙個可以被多個任務呼叫的過程,任務在呼叫時不必擔心資料是否會出錯。...