代码:
#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);
我们可以清楚地看到语法中的处理函数是指针,但在实际代码中,我们不使用指针一个功能。那是为什么?
答案 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()
。很多事情都在信号处理的背后。