linux可重入函数

来源:互联网 发布:社会信用基础数据库 编辑:程序博客网 时间:2024/05/16 05:50
 


可重入函数这一概念早有接触,但一直未有系统的理解,最近阅读《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内核》上有较详细的讲解。

原创粉丝点击