在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)
//聲明一個返回值爲函數指針的函數