[10 信号]信号处理函数内必须使用可重入函数

1 可重入函数

进程在捕捉到信号并对其进行处理时,进程正在执行的正常指令就被信号处理程序临时中断。进程首先执行信号处理程序的指令,然后从信号处理程序返回则继续执行在捕捉信号前正在执行的正常指令序列。
例如,如果进程正在执行malloc,在其堆中分配存储空间,而此时捕捉到信号,信号处理程序中又调用malloc,则可能对进程造成破坏,因为malloc通常为它分配的存储区维护一个李链表,而信号处理程序中又调用malloc,可能会破坏该链表。
再例如,若进程在执行getpwnam,因为getpwnam函数将结果存放在静态存储区,而此时捕捉到信号,信号处理程序中又调用getpwnam,则返回给调用者的信息可能被信号处理程序的信息覆盖。
Single UNIX Specification说明了在信号处理函数内保证调用安全的函数。这些函数被称为可重入函数,也被称为异步信号安全函数。
可重入函数如下表:

abort

faccessat

linkat

select

socketpair

accept

fchmod

listen

sem_post

stat

access

fchmodat

lseek

send

symlink

aio_error

fchown

lstat

sendmsg

symlinkat

aio_return

fchownat

mkdir

sendto

tcdrain

aio_suspend

fcntl

mkdirat

setgid

tcflow

alarm

fdatasync

mkfifo

setpgid

tcflush

bind

fexecve

mkfifoat

setsid

tcgetattr

cfgetispeed

fork

mknod

setsockopt

tcgetpgrp

cfgetospeed

fstat

mknodat

setuid

tcsendbreak

cfsetispeed

fstatat

open

shutdown

tcsetattr

cfsetospeed

fsync

openat

sigaction

tcsetpgrp

chdir

ftruncate

pause

sigaddset

time

chmod

futimens

pipe

sigdelset

timer_getoverrun

chown

getegid

poll

sigemptyset

timer_gettime

clock_gettime

geteuid

posix_trace_event

sigfillset

timer_settime

close

getgid

pselect

sigismember

times

connect

getgroups

raise

signal

umask

creat

getpeername

read

sigpause

uname

dup

getpgrp

readlink

sigpending

unlink

dup2

getpid

readlinkat

sigprocmask

ulinkat

execl

getppid

recv

sigqueue

utime

execle

getsockname

recvfrom

sigset

utimensat

execv

getsockopt

recvmsg

sigsuspend

utimes

execve

getuid

rename

sleep

wait

_Exit

kill

renameat

socketmark

waitpid

_exit

link

rmdir

socket

write

注意,即使信号处理程序里调用了上表里的函数,但是由于每一个线程只有一个errno变量,所以信号处理程序可能会修改其原先的值。例如,main函数设置了errno的值,而信号处理函数调用了read函数,从而可能更改了errno的值。因此,作为一个通用的规则,当信号处理函数调用上表中的函数时,应该在调用前保存errno,在调用后恢复errno。

2 信号处理函数内必须使用可重入函数

如下程序的信号处理程序my_alarm里调用非可重入函数getpwnam,alarm函数每3秒产生一次SIGALRM信号。

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>

static void my_alarm(int signo)
{
    struct passwd    *rootptr;

    printf("in signal handler >>> \n");
    if ((rootptr = getpwnam("root")) == NULL)
        printf("getpwnam(root) error");
    printf("signal: pw_name = %s\n", rootptr->pw_name);
    printf("in signal handler done <<<\n");
    /* 继续设置3s一次SIG_ALARM,否则SIG_ALARM信号不再有 */
    alarm(3);
}

int main(void)
{
    struct passwd    *ptr;
    char  *loginName = "hyh";

    signal(SIGALRM, my_alarm);
    /* 设置SIG_ALARM,3s一次 */
    alarm(3);
    for ( ; ; ) {
        /* getpwname:获取loginName登录信息  */
        if ((ptr = getpwnam(loginName)) == NULL)
            printf("getpwnam error");
        sleep(1);
        printf("pw_name = %s\n", ptr->pw_name);
        if (strcmp(ptr->pw_name, loginName) != 0)
            printf("return value corrupted!, pw_name = %s\n", ptr->pw_name);
    }
}

测试结果如下:

pw_name = hyh
pw_name = hyh
in signal handler >>>
signal: pw_name = root
in signal handler done <<<
pw_name = root
return value corrupted!, pw_name = root
pw_name = hyh
pw_name = hyh
in signal handler >>>
signal: pw_name = root
in signal handler done <<<
pw_name = root
return value corrupted!, pw_name = root
pw_name = hyh
pw_name = hyh
in signal handler >>>
signal: pw_name = root
in signal handler done <<<
pw_name = root
return value corrupted!, pw_name = root
pw_name = hyh
...

可以看到非可重入函数getpwnam的pw_name在main函数时设置的是hyh,信号处理函数又再次调用getpwnam后设置为root,结果被覆盖了。
所以请注意,信号处理函数应该使用可重入函数,禁止使用非可重入函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值