Linux--信号

2023-11-15

信号入门

生活角度的信号

​ 你在网上买了很多商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该如何处理快递,也就是你能识别快递。当快递到了楼下,也收到了快递到来的通知,但是突然你有点事,需要过几分钟之后才能取快递。也就是说取快递的行为并不是一定要立即执行。在收到通知到拿到快递期间,是有一个事件窗口的,在这段时间,你并没有拿到快递,但是本质上是你记住了“有一个快递要去取”。当时间合适的时候,顺利拿到快递之后,就要处理快递了。而处理快递一般方式有三种:1. 执行默认动作(拆开快递)、2. 执行自定义动作(这是送给别人的东西,直接拿去送) 3. 忽略快递(快递拿上来之后,放到一边)

技术应用角度的信号
  1. 用户输入命令,在shell下启动一个前台进程
    • 用户按Ctrl c ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号发送给目标前台进程
    • 前台进程收到型号,引起进程退出
注意
  1. ctrl C产生的信号只能发给前台进程。一个命令后面加一个&可以把进程放到后台运行。这样shell不必等待进程结束就可以接收新的命令,启动新的进程
  2. shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接受到像ctrl c这种控制键产生的信号
信号概念

信号是进程之间事件异步通知的一种方式,属于软中断

使用kill -l命令可以查看系统定义的信号列表

在这里插入图片描述

每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定义#define SIGINT 2,编号34以上的是实时信号

信号处理常见的方式
  1. 忽略此信号
  2. 执行该信号的默认处理动作
  3. 提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉一个信号

产生信号

信号的产生方式有很多种

1. 通过终端按键产生信号

SIGINT的默认处理动作时终止进程,SIGQUIT的默认动作是终止进程并且Core Dump

Core Dump

当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常时因为有bug,比如非法访问导致段段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程Resource(这个信息保存在PCB中)。默认是不允许产生core文件的。

  • 使用ulimit -a可以查看所有进程的Resource limit
  • 使用ulimit -c 1024可以允许core文件最大为1024k

在这里插入图片描述

在这里插入图片描述

使用core dump进行事后调试
#include<stdio.h>
#include<stdlib.h>
int main()
{
    //  一个数除以0是错误的
    int a = 10 / 0;
    return 0;
}

生成core文件

在这里插入图片描述

进行事后调试

在这里插入图片描述

2. 系统调用函数向进程发送信号

首先在后台执行死循环程序,然后用kill命令给他发送SIGEGV信号

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 int main()
  4 {
  5   // 这里写一个死循环       
  6   while(1)
  7   {
  8    
  9   }
 10   return 0;
 11 }

在这里插入图片描述

程序运行起来的进程收到了信号,终止了进程,并且生成了core文件

在这里插入图片描述

kill命令是调用kill函数实现的,kill函数可以给以指定的进程发送指定的信号。raise函数可以给当前进程发送指定的信号(自己给自己发送信号)

#include<signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1
3. 由软件产生信号
#include<unistd.h>
unsigned int alarm(unsigned int seconds);

用途:
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发送`SIGALRM`信号,该信号的默认处理动作是终止当前进程
返回值:
这个函数的返回值是0或者以前设定的闹钟还余下的秒数
4. 硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后向当前进程发送适当的信号。例如,当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释 为SIGSEGV信号发送给进程

信号捕捉
1 #include<stdio.h>  
  2 #include<signal.h>  
  3   
  4 void handler(int sig)  
  5 {  
  6   printf("this is %d signal\n", sig);  
  7 }  
  8   
  9 int main()  
 10 {
 11   // 捕捉2号信号,采用自定的处理方式
 12   signal(2, handler);
 13   while(1);     
 14   return 0;
 15 }

在这里插入图片描述

阻塞信号

1. 信号与其他相关常见概念
  • 实际执行信号得处理动作称为信号递达
  • 信号从产生到递达之间得状态,称为信号未决
  • 进程可以选择阻塞某个信号
  • 被阻塞得信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达动作
  • 注意阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作
在内核中的表示

回到上面,当我们收到快递的时候,我们有很多种选择,可以马上去拿,可以让快递小哥放到一个地方过一会去拿,也可以直接 拒收。为什么我们可以等一一段时间再去处理快递的事情呢?本质上,是因为我们记住了这样一件事,我们有一个快递在等我们去处理。同理,信号的处理也是一样,可以立即处理,也可以等一会再去处理,本质上是操作系统记住有这样一个信号在等待处理。所以在操作系统中一定有一种结构用来存放这些信号

在这里插入图片描述

也就是说,在内核中有这样三张表,用来存放信号的状态

  • block表:本质上是位图结构,把它想像成为一个二进制数,位置对应的就是信号的编号,对应位置上的数据就代表信号是否被阻塞,如果对应比特位上是1,就表示该信号被阻塞,信号不会被递达
  • pending表:代表OS是否受到该信号,同样是位图结构。比特位的位置代表的就是哪一个信号,比特位的内容(0, 1),代表的就是是否收到了信号
  • handler表:就是对信号的处理方式,有三种,默认处理(SIG_DFL),忽略该信号(SIG_IGN),还有一种方式就是自定义的方式处理信号
sigset_t

从上图看来,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决阻塞标志可以用相同的数据烈性sigset_t来存储。sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态

信号集操作函数

sigset_t类型对于每种信号用一个比特位表示有效或无效,至于这个类型内部是如何存储这些bit的则依赖于系统实现。使用者也只能调用系统接口来操作sigset_t变量

#include<signal.h>
int sigemptyset(sigset_t *set);
/*
作用:将由set指定的信号集初始化为空,并排除改集合的所有信号
*/

int sigfillset(sigset_t *set);
/*
 作用:将set指定的信号集初始化为full,包含所有信号
*/

int sigaddset(sigset_t *set, int signum);
/*
作用:向集合中添加信号
*/
int sigdelset(sigset_t *set, int signum);
/*
作用:向集合中删除信号
*/
int sigismember(const sigset_t *set, int signum);
/*
作用:测试某种信号是否在集合中
*/

int sigpending(sigset_t *set);
/*
作用:不对pending位图做修改,而只是单纯的获取进程pending位图
*/

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
/*
如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字(block位图),参数how指示该如何修改,如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前信号屏蔽字为mask
*/
SIG_BLOCK: set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set
SIG_UNBLOCK:  set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set
SIG_SETMASK:  设置当前信号屏蔽字为set所指向的值,相当于mask=set

举个例子

#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

void show_pending(sigset_t set)
{
  int i = 0;
  for(i =0 ; i < 32; ++i)
  {
    int ret = sigismember(&set, i);
    if(ret == 1)
    {
      printf("%d",1);
    }else{
      printf("%d",0);
    }
  }
  printf("\n");
}

void handler(int signo)
{
  printf("收到%d信号\n", signo);
}


int main()
{
  // 定义一个sigset_t变量
  signal(2,handler);
  sigset_t set, pending, oset;

  // 向set中添加2号信号
  sigaddset(&set, 2);
  // 阻塞掉2号信号
  sigprocmask(SIG_BLOCK,&set,&oset);
  int count = 0;
  while(1)
  {
     // 初始化set为空
     sigemptyset(&pending);
     // 查看pending位图
     sigpending(&pending);
     show_pending(pending);
     sleep(1);
     ++count;
     if(count == 20)
     {
       // 恢复对2号信号的阻塞
       sigprocmask(SIG_UNBLOCK, &set, NULL);
       printf("恢复2号信号的,可以被递达\n");
     }
  }
  return 0;
}

在这里插入图片描述

捕捉信号

要理解清楚信号是怎么被捕捉的需要明白两个概念:内核态用户态

内核态:执行OS的代码和数据时,计算机所处的状态就叫做内核态。OS的代码的执行全部都是在内核态

用户态:用户代码和数据被访问或者执行的时候,所处的状态。我们自己写的代码全部都是在用户态执行的

在这里插入图片描述

我们知道,每一个进程PCB都有一个结构用来存放地址空间,地址空间分为两个大部分,1个G的内核空间3个G的用户空间,这两个空间分别有自己的页表映射到物理内存中,用户几页表是每个进程 独有的一张表,而系统级页表是所有进程公用的一张页表。

进程之间无论如何切换,我们都能保证一定能找到同一个OS,因为我们每个进程都有3~4G的地址空间,使用同一张内核页表

在这里插入图片描述

信号的处理

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux--信号 的相关文章

随机推荐

  • SSH正向连接和反向连接

    ssh命令是openssh套件中的客户端连接工具 可以给予ssh加密协议实现安全的远程登录服务器 反向连接是什么 平时大多数使用ssh命令是控制端主机主动连接受控端主机 通过这个连接控制端主机可以主动的向受控端主机发送一些请求 这称为正向连
  • 如何使用 Pylint 来规范 Python 代码风格

    本文转载至 http www ibm com developerworks cn linux l cn pylint Pylint 是什么 Pylint 是一个 Python 代码分析工具 它分析 Python 代码中的错误 查找不符合代码
  • python pymysql emoji表情插入mysql数据库异常记录报错 pymysql.err.InternalError

    在数据库存储微信小程序用户昵称时候 发现用户昵称使用emoji表情时候就存不了数据库中间报错 pymysql err InternalError 1366 Incorrect string value xF0 x9F x98 x81 xF0
  • STM32F429串口1配置

    static void ConfigUART u32 bound GPIO InitTypeDef GPIO InitStructure GPIO InitStructure用于存放GPIO的参数 USART InitTypeDef USA
  • gethostbyname() -- 用域名或主机名获取IP地址

    http hi baidu com zengzhaonong item 87d9d296d0824cbb82d29570 include
  • leetcode236—二叉树的最近公共祖先(递归/深搜/理解)

    给定一个二叉树 找到该树中两个指定节点的最近公共祖先 百度百科中 最近公共祖先的定义为 对于有根树 T 的两个节点 p q 最近公共祖先表示为一个节点 x 满足 x 是 p q 的祖先且 x 的深度尽可能大 一个节点也可以是它自己的祖先 深
  • 解决CSS引用字体跨域问题

    最近一个需求客户要求换字体需要引入字体 开始没有注意 后来发现会出现跨越现象 特别是在手机上很明显 通过解决尝试终于解决 希望可以帮到大家 1 解决方案就是将文字设置为 base64 编码 字体转base64编码网址 点击进去 下载文件解压
  • oracle wait class user i/o,[ORACLE]管理方面的脚本收集

    1 查询AWR相关的视图名称 SELECT table name FROM dba tables t WHERE table name LIKE WRH AND NOT EXISTS SELECT x FROM dba tab column
  • 英文键盘盲打最快速练习口诀和方法

    下面我提供几种在新建的文档 如用于处理文字的word 里进行英文盲打的练习参考资料 以一天练习四个字母计算 最多7天你的盲打就基本练习成功了 下面的口诀 能帮助你快速记住键盘字母的排列顺序 爱上一个不爱回家的人 七 q 碗 w n 鹅 肉
  • Map遍历取值的五种方式

    方法1 Set set map keySet for Object o set System out println o map get o 方法2 Set set map keySet Iterator iterator set iter
  • 准备WebUI自动化测试面试?这30个问题你必须掌握(二)

    本文共有11000字 包含了后十五个问题 如需要前十五个问题 可查看文末链接 16 在WebUI自动化测试中 你如何处理验证码或图像识别的问题 1 人工识别 一种简单但费时费力的方法是使用人工手动识别验证码 测试人员可以手动输入验证码 将其
  • IntelliJ IDEA中谷歌打开页面,出现windows 找不到文件chrome

    1 右击桌面上的chrome浏览器图标 找到属性 gt 快捷方式 gt 目标 复制路径 即chrome浏览器 exe文件的路径 2 打开IntelliJ IDEA软件 找到file gt settings gt 找到Web Browsers
  • Java实现国密算法SM2,SM3,SM4,并且实现ECB和CBC模式

    代码中实现了电码本ECB模式和密文分组连接CBC模式 SM3 java和SM4 java为算法实现类 utils的都是根据实现类写的工具 可以根据需要调用杂凑算法SM3的杂凑功能获得杂凑值 SM4 java中 sm4 crypt ecb S
  • iOS objc_msgSend iOS too many arguments in function call 报错解决方案

    Build Settings gt 搜索 objc gt 设置 Enable Strict Checking of objc msgSend Calls 为 NO
  • [django项目] 利用elasticsearch实现搜索功能

    新闻搜索 I 搜索功能分析 本节我们来完成新闻搜索功能 首先让我们来思考一下 要做一个通过关键词搜索文章的功能 需要搜索哪些字段 以及使用什么技术方案呢 既然我们是准备做新闻博客网站 那我们就可以拿同类型网站的做一下对比 例如CSDN 简书
  • docker系列-搭建本地私有仓库-registry容器的各种坑

    总结的坑 a 关注daemon json的书写格式 一句话可以错好几个点 b tag要清楚的表示registry服务器的信息 才能push上传成功 不是可有可无的信息 c tag中有版本号要清楚的写上 系统自动补全的是用latest 搭建过
  • RPC

    RPC 远程过程调用 是什么 简单的说 RPC就是从一台机器 客户端 上通过参数传递的方式调用另一台机器 服务器 上的一个函数或方法 可以统称为服务 并得到返回的结果 RPC 会隐藏底层的通讯细节 不需要直接处理Socket通讯或Http通
  • 安卓pwn - De1taCTF(BroadcastTest)

    BroadcastTest 背景 逆向APK可知程序中仅有MainActivity Message和三个Receiver类 前者实现了一个Parcelable类 后三个则是广播 其中Receiver1是exported的 接收并向Recei
  • jsts 学习

    性能问题一直困扰了我很长的时间 今天听同事介绍了一个网站 感觉视角开阔了许多 一直做GIS开发 原来不只是java有jts包 原来javascript也有这样的一个包 叫做jsts 这个包的功能跟java里面的jts包差不多 前段提供空间关
  • Linux--信号

    文章目录 信号入门 生活角度的信号 技术应用角度的信号 注意 信号概念 使用kill l命令可以查看系统定义的信号列表 信号处理常见的方式 产生信号 1 通过终端按键产生信号 Core Dump 使用core dump进行事后调试 2 系统