可重入函数(reentrant function)

(转载)http://blog.163.com/xu_jin_rong/blog/static/1491966220086775017178

由于cublog系统的缘故,将前段时间写的一篇blog文章再次贴上。

 

可重入函数这一概念早有接触,但一直未有系统的理解,最近阅读《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

 

 

纵观上表,我们可以看出,有不少系统调用函数并没有出现,换言之也就是非可重入性函数。函数不可重入的原因主要如下:

<!--[if !supportLists]-->(1)       <!--[endif]-->函数使用了static静态数据结构

如:struct passwd *getpwuid(uid_t uid);

struct passwd *getpwnam(const char *name);

struct passwd *getpwent(void);

以上3个函数都是返回一个指向passwd结构的指针,而该passwd结构通常都是函数中static变量,其内容在每次调用以上函数时都会被重写。因此,当进程主程序与信号处理程序中均调用了以上函数时,冲突就产生了。

<!--[if !supportLists]-->(2)       <!--[endif]-->函数调用了malloc和free函数,正如文章最开始所提到的;

<!--[if !supportLists]-->(3)       <!--[endif]-->函数为标准I/O的库函数,因为大多数的标准I/O库函数的实现都使用了global全局数据结构;

因此,若要写可重入性函数的做法通常是我们在函数中只修改局部变量,而不改变全局变量,或尽量不使用全局变量、静态static变量。

事实上,与可重入性函数(reentrant function)对应的还有可重入内核(reentrant kernel),其区别和联系在《深入理解Linux内核》上有较详细的讲解。

你可能感兴趣的:(function)