不确定如何调用信号处理程序

时间:2014-07-30 15:10:29

标签: signals

代码:

#include <signal.h>

static void sigHandler(int sig) {
    printf("Ouch!\n");
}

int main(int argc, char *argv[])
{
    int j;

    if (signal(SIGINT, sigHandler) == SIG_ERR)
        perror("signal");

    for (j = 0; ; j++) {
        printf("%d\n", j);
        sleep(3);
    }
}

我不确定代码是如何工作的。 sigHandler明确地接受一个整数的参数。但是,在if语句中我们写(signal(SIGINT, sigHandler) == SIG_ERR)。很明显,当我们调用sigHandler时,我们不会传入一个整数值,为什么程序不告诉我们这是一个错误?为什么程序仍然按预期工作?此外,我不确定“信号”功能如何工作。

根据linux上的Kerris教科书,信号函数的语法如下void ( *signal(int sig, void (*handler)(int)) ) (int);我们可以清楚地看到语法中的处理函数是指针,但在实际代码中,我们不使用指针一个功能。那是为什么?

4 个答案:

答案 0 :(得分:2)

  

显然,当我们调用sigHandler时,我们不会传递整数值,那么为什么程序没有告诉我们它是错误的呢?为什么程序仍然按预期工作?此外,我不确定&#34;信号&#34;功能也可以。

当然,&#34;我们&#34;不要打电话给信号处理程序 - 系统在收到信号后会这样做。 signal()系统调用只是安装处理程序:&#34; signal()将信号signum的处理设置为handler,它是SIG_IGN,SIG_DFL或程序员定义函数的地址(&# 34;信号处理程序&#34;)。&#34;你去吧。

顺便说一下。再次从手册页引用:&#34; signal()的行为在UNIX版本中有所不同,并且在不同版本的Linux中也有不同的历史差异。避免使用:改为使用sigaction(2)。&#34;

  

我们可以清楚地看到语法中的处理函数是指针,但在实际代码中,我们不使用指向函数的指针。那是为什么?

编译器会自动进行必要的转换。

答案 1 :(得分:1)

你需要刷新C ......

  

但是,在我们编写的if语句中(signal(SIGINT,sigHandler)== SIG_ERR)。很明显,当我们调用sigHandler时,我们不会传入整数值

该声明并未调用sigHandler。它调用signal。括号是C函数调用语法的必需部分,因此sigHandler没有函数调用。

  

...信号函数的语法如下:void(* signal(int sig,void(* handler)(int)))(int);我们可以清楚地看到语法中的处理函数是一个指针,但在实际代码中,我们不使用指向函数的指针。那是为什么?

当您使用不带括号的函数名时,您得到的是指向该函数的指针。所以这是使用函数指针。

答案 2 :(得分:0)

  

显然,当我们调用sigHandler时,我们不会传递整数值,那么为什么程序没有告诉我们它是错误的呢?

该行根本没有调用 sigHandler;它将它作为函数指针传递给signal()。用这种方式写得更清楚:

if (signal(SIGINT, &sigHandler) == SIG_ERR)

注意&#34;地址&#34;运算符&。但是,在这种情况下,它实际上是可选的 - 只是函数的名称将用作函数指针。

为了解释一下,编译器会检查指向的函数的签名是否与signal()的参数匹配。如果通过,则在运行时系统可以根据需要使用int参数调用它。 signal()函数只是告诉系统有关处理程序的一种方法。

答案 3 :(得分:0)

信号处理程序类型相当棘手。我认为它的工作方式如下:当编译器遇到这一行时:

static void sigHandler(int sig) {

它会创建一个名为&#34; sigHandler&#34;的符号。类型为void (*)(int)。您的signal()示例声明(设置信号处理函数的系统调用)具有原型参数void (*handler)(int)。 &#34;处理程序&#34; token只是一个虚拟的:你可以为它添加任何有效的C语言标识符,或者什么都不是。

当编译器到达signal(SIGINT, sigHandler)的实际调用时,它会检查是否存在正确类型的符号sigHandler,并且它在调用{上方}时定义了这样的符号。 {1}}。有时在链接(静态和/或动态)期间,signal()的实际物理地址会进入代码。

信号处理的实际机制也很棘手。

程序开始运行后,sigHandler的物理地址作为参数传递给系统调用sigHandler,以及整数2,即SIGINT清单常量的值。存在函数指针的地方 - 它是运行时signal()的物理地址。 Unix / Linux内核将该地址保存在与将SIGINT(按惯例或标准为2)与该物理地址匹配的进程相关联的某些数据结构中。

如果SIGINT到达您的进程,Unix / Linux内核会安排您的进程停止,然后执行诸如将sigHandler()的堆栈帧放在一起,其中包含值2(SIGINT) sigHandler()的{​​{1}}形式参数,更改堆栈指针寄存器以匹配新的堆栈帧,将指令指针寄存器更改为调用int sigint时存储的物理地址。执行的下一条指令是sigHandler()。很多事情都在信号处理的背后。