在linux-0.11内核中,我们找到signal函数原型:
void (*signal(int _sig, void (*_func)(int)))(int);
这个函数很难理解,先复习看一些基础知识。
1、函数指针定义
int (*f)(int); //定义f为 有一个int类型参数,返回值为int的 函数指针
int p(int); // 我们可以通过 f = &p; 对f进行赋值。
char ( *pf(long) )(int) {} //定义pf为有一个long类型参数,返回值为(有一个int类型参数,返回值为char类型函数指针) 的函数
那么void (*signal(int _sig, void (*_func)(int)))(int);
即是 声明了一个带有2个参数,返回值为带有一个int类型参数,返回值为void的函数指针 的函数
2、通过小例子进一步学习
#include <stdio.h>
void pt(int){
printf("2\n");
}
void (*f(int, void (*fun)(int)))(int){
return fun;
}
int main(){
f(0, pt);
printf("----\n");
(*f(0, pt))(0);
}
这个小程序的输出为:
f(0, pt); 调用后,返回fun的地址,即将fun的地址推送到eax, 但是并未执行。
(*f(0, pt))(0); 调用后, 将eax的地址作为一个函数进行调用, 所以这样的输出就不难理解了。
3、signal源码分析
signal通过int0x80调用系统中断sys_signal. 实现和上面的f基本一样, 其实f就是signal的简化版。
int sys_signal(int signum, long handler, long restorer)
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL)
return -1;
tmp.sa_handler = (void (*)(int)) handler;
tmp.sa_mask = 0;
tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
tmp.sa_restorer = (void (*)(void)) restorer;
handler = (long) current->sigaction[signum-1].sa_handler;
current->sigaction[signum-1] = tmp;
return handler;
}
这个函数将signal(SIG_NUM, handle)传递的参数在进程上下文(信号处理表)中进行注册, 同时返回旧的handle地址。
该函数和上面的f一样,只是将handle地址存放到eax寄存器,当做调用的返回, 并未实际执行函数。
4、更好的方法
typedef void (*__sighandler_t) (int);
//引入 __sighandler_t 类型 为 一个带int类型参数,返回值为void的函数指针 的关键字
extern __sighandler_t __sysv_signal (int __sig, __sighandler_t __handler)
//声明一个返回值为函数指针的函数