信号的影响

更新时间:
2024-01-09
下载文档

信号的影响

前面介绍的信号是软中断,主要是由于信号的发送和中断一样具有异步性和随机性的特点。

系统调用中断

如果线程在某些慢速系统调用的阻塞期间捕捉到一个信号,那么此时的系统调用就会被中断,并且返回错误号和设置 errno 为 EINTR。属于这一类的系统调用包括:

  • POSIX 消息队列调用:mq_receive 函数、mq_send 函数。
  • POSIX AIO 调用:aio_suspend 函数。
  • 信号调用:sigsuspend 函数、pause 函数、sigtimedwait 函数、sigwaitinfo 函数。
  • 定时器调用:nanosleep 函数、sleep 函数。

信号对上述系统调用的作用,可能正是设计所期望的,也可能是设计必须避免的。无论哪种情况,完善的系统应该充分考虑这种影响。如果要避免信号对系统调用的影响,就要采取一定的措施来重新启动系统调用,在 4.2BSD 中,程序能够选择自动恢复被信号中断的系统调用,SylixOS 支持这一特点,只要在安装信号处理函数时设置 SA_RESTART 标志,系统将会自动判断并恢复被中断的系统调用。

如下表给出了部分 SylixOS 中能被信号中断的系统调用:

函数名描述
nanosleep使线程睡眠一个指定的时间(纳秒级)
usleep使线程睡眠一个指定的时间(微秒级)
sleep使线程睡眠一个指定的时间(秒级)
mq_sendPOSIX 消息队列发送函数
mq_timedsendPOSIX 消息队列发送函数,带超时(时间为绝对时间)
mq_reltimedsend_npPOSIX 消息队列发送函数,带超时(时间为相对时间)
mq_receivePOSIX 消息队列接收函数
mq_timedreceivePOSIX 消息队列接收函数,带超时(时间为绝对时间)
mq_reltimedreceive_npPOSIX 消息队列接收函数,带超时(时间为相对时间)
sem_waitPOSIX 信号量阻塞函数
sem_timedwaitPOSIX 信号量阻塞函数,带超时(时间为绝对时间)
sem_reltimedwait_npPOSIX 信号量阻塞函数,带超时(时间为相对时间)

函数可重入影响

线程捕捉到信号并对其进行处理时,正在执行的正常指令序列就被信号处理程序临时中断,它首先执行该信号处理函数中的指令。如果从信号处理程序返回,则继续执行在捕捉到信号时正在执行的正常指令序列(这类似于发生硬件中断时所做的)。但在信号处理函数中,不能判断捕捉到信号时线程执行到何处。如果正在执行 malloc 在其堆中分配另外的存储空间,而此时由于捕捉到信号而插入执行该信号处理程序,其中又调用 malloc 函数,这时可能会对正在执行的上下文造成破坏。

Single UNIX Specification 说明了在信号处理程序中保证调用安全的函数,这些函数是可重入的。除了可重入以外,在信号处理操作期间,它会阻塞任何会引起一致的信号发送,下表列出了这些异步信号安全函数:

函数名函数名函数名函数名函数名函数名
abortdup2getpidrecvsigfillsettimes
acceptexeclgetppidrecvfromsigismemberumask
accessexeclegetsocknamerecvmsgsignaluname
aio_errorexecvgetsockoptrenamesigpendingunlink
aio_returnexecvegetuidrmdirsigprocmaskutime
aio_suspend_Exitkillselectsigqueueutimes
alarm_exitlistensem_postsigsuspendwait
bindfchmodlseeksendsleepwaitpid
cfgetispeedfchownlstatsendmsgsocketwrite
cfggetospeedfcntlmkdirsendtosocketpair
cfsetispeedfdatasyncmkfifosetgidstat
cfsetospeedfstatmknodsetpgidsymlink
chdirfsyncopensetsidtcdrain
chmodftruncatepausesetsockopttcflush
chowngetegidpipesetuidtcgetattr
clock_gettimegeteuidpollshutdowntcsetattr
closegetgidpselectsigactiontime
connectgetgroupsraisesigaddsettimer_getoverrun
creatgetpeernamereadsigdelsettimer_gettime
dupgetpgrpreadlinksigemptysettimer_settime

下面我们看一个实例,在信号处理函数 int_handler 中调用 getpwnam 函数来获得用户名,int_handler 每一秒被调用一次。

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

void int_handler (int  signum)
{
    struct passwd *ptr;

    fprintf(stdout, "Alarm signal!\n");
    if ((ptr = getpwnam("root")) == NULL) {
        fprintf(stderr, "getpwnam error.\n");
    }
    alarm(1);
}

int main (int argc, char *argv[])
{
    struct passwd *ptr;

    signal(SIGALRM, int_handler);
    alarm(1);

    for (;;) {
        if ((ptr = getpwnam("sylixos")) == NULL) {
            fprintf(stderr, "getpwnam error.\n");
        }
        if (strcmp(ptr->pw_name, "sylixos") != 0) {
            fprintf(stderr, "ptr->pw_name: %s\n", ptr->pw_name);
        }
    }
    return  (0);
}

运行该程序,会发现程序结果具有随机性。一般情况,信号处理函数被调用几次后,程序将可能会发生异常由信号 SIGSEGV 终止结束,也可能 main 函数还能正常运行,此时系统 Shell 却产生异常。

从此实例中可以看出,如果在信号处理函数中调用一个不可重入函数,则结果是不可预测的。

文档内容是否对您有所帮助?
有帮助
没帮助