linux内核分析之signal.c函数

来源:互联网 发布:儿童舞蹈包淘宝 编辑:程序博客网 时间:2024/05/03 01:41

该文件分装了信号处理函数
#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;
}

 

原创粉丝点击