1. 程式人生 > >可重入函式(reentrant function)

可重入函式(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 核心》上有較詳細的講解。