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的原理来制造病毒程序。
|