该文件分装了信号处理函数
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <signal.h>
用volatile修饰符修饰函数,表示该函数不会返回,并且
保证编译器不会给出告警
volatile void do_exit(int error_code);
取得当前进程被阻塞的信号掩码
int sys_sgetmask()
{
return current->blocked;
}
设置当前进程的被阻塞的信号掩码
int sys_ssetmask(int newmask)
{
保存旧的被阻塞的信号掩码,供返回
int old=current->blocked;
SIGKILL信号不能被阻塞
current->blocked = newmask & ~(1<<(SIGKILL-1));
return old;
}
将内核数据区的数据复制到用户数据区中,以字节为单位进行复制。
开始地址from,目标地址to
static inline void save_old(char * from,char * to)
{
int i;
/
关于verify_area函数的定义我摘操在下面了。
验证给定地址是否越界
void verify_area(void * addr,int size)
{
unsigned long start;
start = (unsigned long) addr;
验证区域大小加上该页起始地址到start的偏移
size += start & 0xfff;
将开始地址进行页边界对齐
start &= 0xfffff000;
从当前进程的局部描述符表中找出用户数据段描述符,然后从描述符中取得段基地址
然后求出线性地址,存入start中
start += get_base(current->ldt[2]);
进行验证,以一页为单位进行
while (size>0) {
size -= 4096;
判断该页的写标志位是否置位
write_verify(start);
start += 4096;
}
}
//
验证用户数据段中申请的to空间是否可写
verify_area(to, sizeof(struct sigaction));
进行复制
for (i=0 ; i< sizeof(struct sigaction) ; i++) {
put_fs_byte(*from,to);
from++;
to++;
}
}
将用过户数据段中的数据复制到内核数据段中
static inline void get_new(char * from,char * to)
{
int i;
进行复制
for (i=0 ; i< sizeof(struct sigaction) ; i++)
*(to++) = get_fs_byte(from++);
}
信号处理的系统调用函数
int sys_signal(int signum, long handler, long restorer)
{
struct sigaction tmp;
进行验证:
1. 信号是否越界
2. 信号是不是不可捕获不可忽略的信号
if (signum<1 || signum>32 || signum==SIGKILL)
return -1;
对sigaction数据结构进行付值
信号处理函数
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;
重新设置新的sigaction结构
current->sigaction[signum-1] = tmp;
return handler;
}
sigaction系统调用函数
int sys_sigaction(int signum, const struct sigaction * action,
struct sigaction * oldaction)
{
struct sigaction tmp;
进行验证:
1. 信号是否越界
2. 信号是不是不可捕获不可忽略的信号
if (signum<1 || signum>32 || signum==SIGKILL)
return -1;
取得旧的sigaction结构
tmp = current->sigaction[signum-1];
因为进程的sigaction结构数组是在内核数据段中,所以调用get_new对新的sigaction结构
进行付值
get_new((char *) action,
(char *) (signum-1+current->sigaction));
如果oldaction不是空,那么将内核数据点中sigaction结构复制到用户数据段中的
结构中。
if (oldaction)
save_old((char *) &tmp,(char *) oldaction);
如果该进程所对应的信号不阻塞自身,那么sa_mask设置为0
否则将自身加入到被阻塞的信号位图中
if (current->sigaction[signum-1].sa_flags & SA_NOMASK)
current->sigaction[signum-1].sa_mask = 0;
else
current->sigaction[signum-1].sa_mask |= (1<<(signum-1));
return 0;
}
该函数是在系统调用0x80最后调用的函数,用来对信号的处理
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;
取得指定当前进程信号行为数组中的sigaction结构
struct sigaction * sa = current->sigaction + signr - 1;
int longs;
unsigned long * tmp_esp;
取得信号处理句柄
sa_handler = (unsigned long) sa->sa_handler;
如果为忽略该信号,直接返回
if (sa_handler==1)
return;
如果为缺省处理方式
if (!sa_handler) {
如果不是SIGCHILD那么久直接退出进程,否则直接忽略
if (signr==SIGCHLD)
return;
else
do_exit(1<<(signr-1));
}
如果该信号标志位设置了SA_ONESHOT,那么将信号处理句柄置为0
表示执行默认动作
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
因为c语言是传值的,所以将原来eip的栈位置存入信号处理函数的地址,也就是说
该函数返回之后,回去执行信号处理函数
*(&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++);
将系统调用前的eip复制到用户堆栈上,以至于信号处理函数返回后可以接着执行
系统调用前的代码段
put_fs_long(old_eip,tmp_esp++);
将该sigaction结构中的被阻塞的信号位图加到当前进程的被阻塞的信号位图中去
current->blocked |= sa->sa_mask;
}