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使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数

MySQL字符串常用函数详解

《MySQL字符串常用函数详解》本文给大家介绍MySQL字符串常用函数,本文结合实例代码给大家介绍的非常详细,对大家学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql字符串常用函数一、获取二、大小写转换三、拼接四、截取五、比较、反转、替换六、去空白、填充MySQL字符串常用函数一、

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

MySql基本查询之表的增删查改+聚合函数案例详解

《MySql基本查询之表的增删查改+聚合函数案例详解》本文详解SQL的CURD操作INSERT用于数据插入(单行/多行及冲突处理),SELECT实现数据检索(列选择、条件过滤、排序分页),UPDATE... 目录一、Create1.1 单行数据 + 全列插入1.2 多行数据 + 指定列插入1.3 插入否则更

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

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 基于