Linux测试程序运行时的信号问题

leovuyo 发布于 2013/11/09 10:25
阅读 506
收藏 1
小弟在学linux不久 参考数目《Linux环境高级编程》

在运行一个小程序时遇到小问题,不太明白,望赐教。

#include<stdio.h>
#include<sys/signal.h>
void fun(int signo){
    printf("%s\n","Divisor can't be zero");
}
int main(){
    int a=1,b=0;
    signal(SIGFPE,fun);
    int c = a/b;
    return 0;
}

代码运行后一直打印 Divisor can't be zero不中断。什么情况?

加载中
0
狮子的魂
狮子的魂

 就看到了一个"不中断"去了. 

@jingweiyoung

1. 如果用户没有SIGFPE信号的处理函数, 内核的执行流程:
arch/x86/kernel/signal.c do_notify_resume()->do_signal()->get_signal_to_deliver(), 这样进程会被get_signal_to_deliver函数杀掉.

2. 如果设置了SIGFPE的处理函数:
arch/x86/kernel/signal.c do_notify_resume()->do_signal()->handle_signal(), 从内核空间返回到用户空间后会直接调用信号处理函数(用户空间出现了除0错误, CPU会自动的切换到内核空间调用do_divide_error函数来处理除0错误), 信号函数执行完毕后, 又会回到上一个指令. 也就是这个处理指令会持续的执行, 信号函数也会持续的调用. 

内核对这个信号的处理方式, 和别的信号有些许区别, 通常使用sigsetjmpsiglongtjmp函数来处理这个异常.(arch/x86/kernel/signal.c中有处理流程):

流程大致如下:

jmp_buf jmpbuf;

void handler_fun( int signo ) {
    siglongjmp(jmpbuf);
}

int main( int argc, char *argv[] ) 
{
    int ret;
    signal(SIGFPE, handler_fun); 
    if (sigsetjmp(jmpbuf, 1) == 0) { //try                              
        int i = 10 / 0;
    } else { // catch                             
        printf("exception handler\n");
    }

    return 0;
}


0
jingweiyoung
jingweiyoung

SIGFPE : 进行数值运算时发生的异常情况,如除以0
signal这个函数表示信号处理方式,当指定的信号到达时,就调用相应的函数,这里你调用了函数fun,所以...

没事可以看看man手册,看看signal到底是怎么用的

jingweiyoung
jingweiyoung
嗯,抱歉我没仔细看你的代码
l
leovuyo
嗯,我想知道为什么一直循环打印"Divisor can't be zero" 这句话而不停止
0
狮子的魂
狮子的魂

终止是内核对接收SIGFPE信号的进程的默认行为, 你已经通过signal函数向内核注册了SIGFPE信号的新处理函数, 默认行为就不会执行了(也就是不会默认终止了). 

在信号处理函数fun中加入exit(1)来自行终止.

0
jingweiyoung
jingweiyoung

引用来自“狮子的魂”的答案

终止是内核对接收SIGFPE信号的进程的默认行为, 你已经通过signal函数向内核注册了SIGFPE信号的新处理函数, 默认行为就不会执行了(也就是不会默认终止了). 

在信号处理函数fun中加入exit(1)来自行终止.


#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

#define INPUTLEN 128

void inthandler( int );


int main(int argc, char *argv[])
{
    int nchars;
    char input[INPUTLEN];

    signal(SIGINT, inthandler);
    do{
        printf("\nType a message\n");
        nchars = read(0, input, (INPUTLEN-1));
        if( -1==nchars )
        {
            perror("read returned an error");
        }
        else{
            input[nchars] = '\0';
            printf("You typed: %s\n", input);
        }
    }while( strncmp(input, "quit", 4)!=0 );


    return 0;
}

void inthandler( int s )
{
    printf("inthandler() | Recived signal [%d], Ctrl -C .. waiting\n", s);
    //没有return或exit

}
请教楼上,SIGFPE系统默认行为是core dump.  SIGINT默认是终止进程。
上面这个程序在调用函数内也没有return或者exit,为什么我每次Ctrl -C的时候inthandler()只执行一次呢?



0
狮子的魂
狮子的魂

@jingweiyoung 发送一次信号, 调用一次信号处理函数. Ctrl+c按下一次调用一次inthandler, 合理啊.

Type a message
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting
^Cinthandler() | Recived signal [2], Ctrl -C .. waiting


jingweiyoung
jingweiyoung
我想说的是跟楼主的问题进行比较,同样没有用return或者exit,楼主的代码为什么不停的在调用fun
0
赵云30
赵云30

很遗憾。在我的这台机器Linux上,并没有输出Divisor can't be zero,程序并没有进入到fun函数中去。

再次证明了我的看法:信号不可靠,在不同的Unix/Linux上表现差别很大,没有人能真正说清楚到底是怎么回事。这次的程序运行结果出现预期了,下次换台机器,换个环境,几乎必然出错。 你不应该编写信号处理函数,你不应该在你的程序里使用信号。上一代程序员犯了太多的错误,没来记得改正就死了,我们不应该继承他们的错误。

拿除零来说,你应该用if判断是不是0,然后做处理;或者你编写一个div函数在函数里判断除数是不是零。

0
狮子的魂
狮子的魂

引用来自“长工”的答案

很遗憾。在我的这台机器Linux上,并没有输出Divisor can't be zero,程序并没有进入到fun函数中去。

再次证明了我的看法:信号不可靠,在不同的Unix/Linux上表现差别很大,没有人能真正说清楚到底是怎么回事。这次的程序运行结果出现预期了,下次换台机器,换个环境,几乎必然出错。 你不应该编写信号处理函数,你不应该在你的程序里使用信号。上一代程序员犯了太多的错误,没来记得改正就死了,我们不应该继承他们的错误。

拿除零来说,你应该用if判断是不是0,然后做处理;或者你编写一个div函数在函数里判断除数是不是零。

有很多原因都会导致SIGFPE信号的发生, 当错误发生时, 内核给你的是一个信号.  除0可以这么解决, 并不是所有的原因都可以这么解决的. 

内核中很多进程之间的通信使用的都是信号. 你的机器中运行的很多软件也一定使用了信号. 信号中还有一个信号值会影响到信号的可靠性....

信号值位于SIGRTMIN和SIGRTMAX之间的信号才是可靠信号....

0
赵云30
赵云30


SIGRTMIN和SIGRTMAX之间叫”系统未定义的信号“而不是可靠信号,你可以kill -l看一下,这些信号系统保留给用户自己用的,实际上我没见人用过,因为我们都觉得信号机制是非常差劲的设计,打乱了的程序流程,你应该听说过以前的人批评GO语句打乱程序流程所以现在没什么人用go语句了,信号处理函数比Go语句的害处有过只而无不及。


如果你在多种Unix系统上都运行过信号程序的话。最后你会发现,还不如自己写if else更为简单方便。

当然信号处理不用自己写,比如SIGSEGV,系统默认会产生Core Dump,你没必要非要改他的默认动作。Bash下的Ctrl + C Ctrl + Z也会产生信号,你也没必要改他的默认动作。

还是那句话:你可以学学各种信号干什么用的,用在什么情况下,但是请不要在你的实际运行的程序里作信号处理,尤其是是大规模服务与很多人的程序,信号处理是导致程序崩溃的一个重大原因。

信号处理这一套东西我看活不过5年,将来的Linux Unix系统都会去掉,改为更为可靠的设计,是什么样的设计呢?我能力有限,设计不出来, 不过我相信会有人设计出来的。 -_-

狮子的魂
狮子的魂
恩,小于SIGRMIN的信号通过kill发送的信号可能会丢失,还是执行默认行为。 信号设计本来就是用来模拟软件中断,自然会打算软件的执行流程,不过这也为确实为编程带来了不便。。。。
返回顶部
顶部