如何在ubuntu 上编译linux-0.11 :
include/signal.h
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGUNUSED 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGSTKFLT 16
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
do_signal函数是内核系统调用中断(int 0X80)中断处理子程序的预处理程序,该函数的主要作用是将信号的处理句柄插入到用户程序堆栈中,系统调用后就会执行。
为什么0x80是系统调用:在代码中有相关设置
//设置系统中断
set_system_gate(0x80,&system_call);
sys_signal由函数库提供,用于恢复系统调用后的返回值和一些寄存器。编译链接时由libc函数库提供,用于 在信号处理程序结束后清理用户态堆栈,并恢复系统调用存放在eax中的返回值。
在system_call.s中,可以看到首先调用的是do_signal进行预处理,再调用sys_signal信号处理 。
_system_call:
cmpl $nr_system_calls-1,%eax // 比较当前的调用号和当前最大调用号 nr_system_calls = 72 0.11 仅仅有72个系统调用
ja bad_sys_call
push %ds
push %es
push %fs
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
call _sys_call_table(,%eax,4) //调用系统调用函数表中对应的系统调用
pushl %eax
movl _current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
ret_from_sys_call:
movl _current,%eax # task[0] cannot have signals
cmpl _task,%eax
je 3f
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
movl signal(%eax),%ebx
movl blocked(%eax),%ecx
notl %ecx
andl %ebx,%ecx
bsfl %ecx,%ecx
je 3f
btrl %ecx,%ebx
movl %ebx,signal(%eax)
incl %ecx
pushl %ecx
call _do_signal //执行do_signal 系统调用
popl %eax
3: popl %eax
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret
由于C函数是传值函数,因此给eip赋值时需要使用'*(&eip)'的形式。
//系统调用中断处理程序中的信号处理程序
void do_signal(long signr,long eax, long ebx, long ecx, long edx,
long fs, long es, long ds,
long eip, long cs, long eflags,
unsigned long * esp, long ss)
{
unsigned long sa_handler;
long old_eip=eip;
/*
struct sigaction sigaction[32];
current->sigaction + signr - 1 指向对应signr的sigaction
*/
struct sigaction * sa = current->sigaction + signr - 1;
int longs;
unsigned long * tmp_esp;
sa_handler = (unsigned long) sa->sa_handler;
//#define SIG_DFL ((void (*)(int))0) /* default signal handling */
//#define SIG_IGN ((void (*)(int))1) /* ignore signal */
if (sa_handler==1)/* ignore signal */
return;
if (!sa_handler) {/* default signal handling */
if (signr==SIGCHLD)//signr=SIGCHLD 没有定义处理函数 则返回
return;
else
//do_exit 处理当前进程退出过程,当前进程已经死亡
do_exit(1<<(signr-1));
}
if (sa->sa_flags & SA_ONESHOT)//该信号句柄仅仅需要使用一次,sa->sa_handler 置空
sa->sa_handler = NULL;
//将信号处理函数插入到用户堆栈。本次系统调用中断(0x80)返回用户程序时会先执行该信号处理函数
//然后继续执行用户程序
*(&eip) = sa_handler;
longs = (sa->sa_flags & SA_NOMASK)?7:8;
*(&esp) -= longs;
verify_area(esp,longs*4);
tmp_esp=esp;
put_fs_long((long) sa->sa_restorer,tmp_esp++);
put_fs_long(signr,tmp_esp++);
if (!(sa->sa_flags & SA_NOMASK))
put_fs_long(current->blocked,tmp_esp++);
put_fs_long(eax,tmp_esp++);
put_fs_long(ecx,tmp_esp++);
put_fs_long(edx,tmp_esp++);
put_fs_long(eflags,tmp_esp++);
put_fs_long(old_eip,tmp_esp++);
current->blocked |= sa->sa_mask;
}
signal()系统调用。类似于sigaction()。为指定的信号安装新的信号句柄(信号处理程序)。 信号句柄可以是用户指定的函数,也可以是SIG_DFL(默认句柄)或SIG_IGN(忽略)。参数signum --指定的信号;handler --指定的句柄;restorer --恢复函数指针,该函数由Libc库提供。用于在信号处理程序结束后恢复系统调用返回时几个寄存器的原有值以及系统 调用的返回值,就好像系统调用没有执行过信号处理程序而直接返回到用户程序一样。 函数返回原信号句柄。
int sys_signal(int signum, long handler, long restorer)
{
设置分配一个信号结构体
struct sigaction tmp;
检索信号范围要在1-32并且不是终止信号
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;
}