linux内核分析之signal.c函数

2024-04-20 20:38

本文主要是介绍linux内核分析之signal.c函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

 

这篇关于linux内核分析之signal.c函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/921262

相关文章

PostgreSQL中rank()窗口函数实用指南与示例

《PostgreSQL中rank()窗口函数实用指南与示例》在数据分析和数据库管理中,经常需要对数据进行排名操作,PostgreSQL提供了强大的窗口函数rank(),可以方便地对结果集中的行进行排名... 目录一、rank()函数简介二、基础示例:部门内员工薪资排名示例数据排名查询三、高级应用示例1. 每

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

全面掌握 SQL 中的 DATEDIFF函数及用法最佳实践

《全面掌握SQL中的DATEDIFF函数及用法最佳实践》本文解析DATEDIFF在不同数据库中的差异,强调其边界计算原理,探讨应用场景及陷阱,推荐根据需求选择TIMESTAMPDIFF或inte... 目录1. 核心概念:DATEDIFF 究竟在计算什么?2. 主流数据库中的 DATEDIFF 实现2.1

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon