分类:
2008-12-08 11:43:21
10.6 reentrant functions
这个话题总是与signal关联在一起。
发生reentrant问题的情况:
1.内存分配,或者处理全局数据时
当我们在malloc一段内存,当我们再用getpwnam来获取用户帐户的信息(这个函数会使用static缓冲),总之当我们在处理一段全局的数据结构的时候(这里的全局值得不仅仅是具有全局的作用域,还包括了全局的生命周期比如static缓冲),我们被一个signal打断,signal handler执行了,在这个handler内部也对该数据进行操作,这个数据就乱套了。Malloc维护的page的list会乱套,getpwnam的static 缓冲会乱套。
2.使用标准库
我们知道,printf等standard i/o library其实使用了自己的全局的缓冲,所以,当我们调用printf的时候,如果收到一个signal, 那么,如果,signal handler里面调用了printf,也会将standard i/o library的缓冲搅乱。
3.Errno
Errno本身是个全局量,所以说,你甚至可以认为说有使用了errno的函数都不是可重入的函数。当然这也要求太高了,所以,有一个建议,在你的signal handler里面,如果有操作要改变errno,那么在signal handler的开头,将errno保存一下,在退出handler之前,将其restore。
4.Longjmp和siglongjmp的作用
我们在处理一个全局数据,此时signal来了,在signal handler里面,没有return,而是调用longjmp或者siglongjmp调到别处执行去了,这样就会使正在被处理的全局数据成处理到了一半。如果我们的signal handler里面的确一定要调用siglongjmp这样的函数,那么我们应该保证我们处理全局数据的时候,该signal不会发生,即我们可以将该signal block一会儿。
上述函数都不是可reentrant的函数。
Single unix specification指定如下函数应该是reentrant函数:
Figure 10.4. Reentrant functions that may be called
from a signal handler |
||||
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 |
下面是一个例子,它使用每秒钟一次的alarm signal,调用getpwname函数:
#include "apue.h"
#include
static void
my_alarm(int signo)
{
struct passwd *rootptr;
printf("in signal handler\n");
if ((rootptr = getpwnam("root")) == NULL)
err_sys("getpwnam(root) error");
alarm(1);
}
int
main(void)
{
struct passwd *ptr;
signal(SIGALRM, my_alarm);
alarm(1);
for ( ; ; ) {
if ((ptr = getpwnam("sar")) == NULL)
err_sys("getpwnam error");
if (strcmp(ptr->pw_name, "sar") != 0)
printf("return value corrupted!, pw_name = %s\n",
ptr->pw_name);
}
}
过一段时间,就会出错,甚至会出现SIGSEGV。