signal是UNIX下最常用的一种通信机制,程序对不同的信号安装处理处理函数,JICAMA支持32个信号,目前还没有平台专用的信号,基本上可以与传统的UNIX兼容。
这是最简单的一个SIGNAL的编程例子,原先的程序来自MINIX:
/* sleep - suspend a process for x sec Author: Andy Tanenbaum */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigalrm(int signo)
{
int *ret;
*ret=(int *)&ret+2;
printf("receive alarm, signo=%d, ret 0x%x\n", signo,*ret);
}
int main(argc, argv)
int argc;
char *argv[];
{
register seconds;
register char c;
seconds = 0;
if (argc != 2) {
printf("Usage: sleep time\n");
exit(1);
}
while (c = *(argv[1])++) {
if (c < '0' || c > '9') {
printf("sleep: bad arg\n");
exit(1);
}
seconds = 10 * seconds + (c - '0');
}
/* Now sleep. */
signal(SIGALRM, sigalrm);
alarm(seconds);
pause();
return(0);
}
这个程序的目的是让程序睡眠seconds秒,然后唤醒,继续运行。在用户接口上,JICAMA和MINIX完全一致,这是为了保障程序的兼容性,下面分析下其中的实现吧,
signal函数原代码:
__sighandler_t signal(int sig, __sighandler_t func)
{
__sighandler_t res;
__asm__("int $0x80":"=a" (res):
"0" (NR_SIGNAL),"b" (sig),"c" ((long)func),"d" ((long)&asm_sig_restore));
return res;
}
__sighandler_t是一个函数指针,原形被定义如下:
typedef void (*__sighandler_t) (int);
signal的目的是为了注册一个信号处理,返回先前该信号的处理句柄,他只有2个参数,sig是要注册的信号,func是当信号发生的情况下执行的函数。
然后alarm设置一个闹钟,注意如果没有signal(SIGALRM, sigalrm);这行代码的话闹钟将会是程序自动退出。pause()将目前的进程挂起,并调度,其他进程运行。
seconds之后,程序得到恢复,进程被标上SIGALRM信号,这时候查找相应的执行函数,如果SIGALRM已经被注册了,则执行注册的程序,在程序开始执行的时候,堆栈构造如下:
esp=_asm_sig_restore
esp+4=信号(signo)
esp+8=保留(???)
esp+12=mask(sigmask)
esp+16=原先的寄存器内容(regs)
他的参数是保存在esp+4,自然是signo,函数执行过后还需要继续往后面的程序运行这个就是恢复函数,否则极有可能抛出一个异常,终止程序运行,恢复内定是由asm_sig_restore来完成的,这是一段汇编代码,它是用NASM写的:
_asm_sig_restore:
add esp,8
mov ebx, [esp+4]
mov ecx, [esp]
mov eax, 75
int 0x80
ret
首先把signo和???里面的内容丢弃,这时候esp的内容指向mask,把mask的内容保存在ecx,把原先寄存器的内容保存在ebx,用于过后的恢复,然后进入内核,内核把其中的寄存器恢复,这样可以继续往后面运行,呵呵,顺便说一句,黑客们完全可以利用_asm_sig_restore的原理来制造病毒程序。