【Linux】进程信号之信号的产生

进程信号 一

  • 一、信号入门
    • 1、信号的一些特性
    • 2、信号的处理方式
      • 信号捕捉初识
    • 3、Linux下的信号
  • 二、信号的产生
    • 1、通过终端按键产生信号
    • 2、调用系统函数向进程发信号
      • a、kill函数
      • b、raise函数
      • c、abort函数
    • 3. 由软件条件产生信号
    • 4、硬件异常产生信号
  • 结语

一、信号入门


什么是信号:信号就是一条消息,它用来通知进程系统中发生了一个某种类型的事件。

信号是多种多样的,并且一个信号对应一个事件,这样才能知道收到一个信号后,到底是一个什么事件,应该如何处理这个信号。

1、信号的一些特性

在这里插入图片描述

  1. 进程在没有收到信号时就已经知道了一个信号应该怎么被处理了,这说明进程能够识别并处理信号
  2. 信号对于进程来说是随时都有可能产生的,因此进程与信号是异步的
  3. 由于进程与信号是异步的,当信号产生时,进程可能正在执行优先级更高的事情,这时进程并不能立即处理信号,需要在合适的时候再进行处理,因此在这个空窗期内信号要能够被保存起来,这说明进程具有记录信号的能力
  4. 进程记录的信号可能有很多个,因此进程需要用一种数据结构去管理所有的信号,在Linux下对于信号的管理采用的是位图结构,比特位的位置代表信号的编号
  5. 所以所谓的发送信号本质就是:直接修改特定进程的信号位图中的特定的比特位。(由0 -> 1
  6. 进程信号的位图结构本质还是属于task_struct里面的数据,因此对于进程信号的位图结构里面的数据的修改,只能有操作系统来完成,即无论有多少种信号产生的方式,最终都必须让OS来完成最后的发送过程!

2、信号的处理方式

  1. 执行默认动作(即操作系统给信号设定的默认动作)
  2. 忽略信号
  3. 执行自定义动作(用户修改了操作系统设定的默认动作,改成了自己想要的动作),操作系统为我们提供一个信号处理函数signal,可以要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉(Catch) 一个信号。

信号捕捉初识

信号捕捉主要是使用signal函数,该函数内部使用了回调函数。

在这里插入图片描述
该函数的作用就是将指定的信号的默认行为更改为执行第二个参数对应的函数,这个函数要求必须是返回值为void参数是int的函数。

  • 参数
    1. 信号的编号。
    2. 回调函数的函数指针。
  • 返回值: 返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)

实例代码:
我们在键盘下按的 Ctrl + C 其实就是2号信号,下面我们尝试对2号信号进行捕捉。

#include <iostream>
#include <signal.h>
#include <unistd.h>

void hander(int sig)
{
    std::cout << "get a signal " << sig << std::endl;
}


int main()
{
    signal(2, hander);
    while (true)
    {
        std::cout << "我正在运行...,我的PID是: " << getpid() << std::endl;
        sleep(1);
    }
    
    return 0;
}

运行结果:

可以看到我们使用 Ctrl + C 已将无法终止进程了,变成了我们自定义的动作了!
在这里插入图片描述

3、Linux下的信号

Linux下我们可以使用kill -l命令列出所有的信号。

在这里插入图片描述

仔细观察我们发现,这里面是没有32 ,33号信号的!其中从1~31号信号是普通信号,34~64是实时信号。(这里我们主要讨论普通信号)

  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在/usr/include/bits/signum.h中找到。
    在这里插入图片描述
  • 对于普通信号默认的处理动作是什么,在man 7 signal中都有详细说明。
    在这里插入图片描述

二、信号的产生

Linux下进程信号的产生是有多种方式的,下面我们就来一起了解一下吧!

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

Linux下输入命令可以在Shell下启动一个前台进程,当我们想要终止一个前台进程时,我们可以按下 Ctrl + C 来进行终止这个前台进程,其实这个 Ctrl + C 也是一个信号,它对应的信号的2号信号SIGINT,这个信号对应的默认处理动作就是终止当前的前台进程。

在这里插入图片描述

  • 用户按下 Ctrl-C ,这个键盘输入产生一个硬件中断 ,被OS获取,解释成信号,发送给目标前台进程,前台进程因为收到信号,进而引起进程退出

  • Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程,同样这样的后台进程也无法使用Ctrl-C 来进行杀死。

  • Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。


关于硬件中断

  • 硬件中断是由硬件设备触发的中断,当硬件设备有数据或事件需要处理时,会向CPU发送一个中断请求,CPU在收到中断请求后,会立即暂停当前正在执行的任务,进入中断处理程序中处理中断请求。

关于软中断

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

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

a、kill函数

在这里插入图片描述
kill函数是操作系统给我们提供的一个系统调用,通过它我们能够给指定的进程发送指定的信号。

  • 参数

    1. 目标进程的pid
    2. 要发送的信号signal
  • 返回值:调用成功就返回 0,调用失败就返回 -1

kill命令其是就是调用kill函数实现的,下面我们也来模拟实现一下kill命令。

实例代码:

#include <iostream>
#include <string>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <signal.h>
#include <sys/types.h>

void Usage(const std::string proc)
{
    std::cout << "Usage:" << std::endl;
    std::cout << "    " << proc << "   信号编号   目标进程" << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(-1);
    }
    
    pid_t pid = atoi(argv[2]);
    int signo = atoi(argv[1]);
    int return_val = kill(pid, signo);
    if (return_val == -1)
    {
        std::cout << "错误码:" << errno << " 错误信息:" << strerror(errno) << std::endl;
    }
    return 0;
}

运行结果:

在这里插入图片描述

b、raise函数

在这里插入图片描述

此函数会向当前进程发送指定的信号

  • 参数: 要发送的信号sig

  • 返回值:调用成功就返回 0,调用失败就返回非0

实例代码:

我们用raise函数给当前进程发送暂停信号19 SIGSTOP ,暂停以后我们可以在命令行中给进程发送继续运行18SIGCONT信号

#include <iostream>
#include <signal.h>
#include <unistd.h>

int main()
{
    sleep(1);
    std::cout << "我要被暂停了,我的PID是:" << getpid() << std::endl;
    
    raise(19);
    
    std::cout << "我要继续运行了,我的PID是:" << getpid() << std::endl;
    return 0;
}

在这里插入图片描述

c、abort函数

在这里插入图片描述

abort函数使当前进程接收到信号而异常终止,abort函数其实是向进程发送6号信号SIGABRT,就像exit函数一样,abort函数总是会成功的,所以没有返回值,值得注意的是就算6号信号被捕捉了,调用abort函数还是会退出进程。

实例代码:

#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>

int main()
{
    std::cout << "begin" << std::endl;
    abort();
    std::cout << "end" << std::endl;

    return 0;
}

在这里插入图片描述

这三个函数只有kill是系统调用,另外两个都是C库函数,它们的功能对比如下:

在这里插入图片描述

3. 由软件条件产生信号

SIGPIPE是一种由软件条件产生的信号,在“管道”中已经介绍过了。这里主要介绍alarm函数和SIGALRM信号。

在这里插入图片描述

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发14号信号SIGALRM信号, 该信号的默认处理动作是终止当前进程。

  • 参数:闹钟的秒数。
  • 返回值:这个函数的返回值有一点特殊,它是是上一次设定的闹钟时间还余下的秒数或者是0(0代表上一次的闹钟没有收到干扰,正确的执行完了)

实例代码:

#include <iostream>
#include <signal.h>
#include <unistd.h>

int main()
{
    alarm(1);
    int count = 0;
    while (true)
    {
        std::cout << count++ << std::endl;
    }
    return 0;
}

在这里插入图片描述

4、硬件异常产生信号

硬件异常产生信号是指硬件产生了错误并以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。

例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

例如下面的代码我们进行除0操作:

#include <iostream>

int main()
{
    int a = 10;
    a /= 0;
    std::cout << a << std::endl;
    return 0;
}

在这里插入图片描述

在编译的时候我们收到了一个警告(除0问题),然后我们不管接着运行我们的代码,然后我们的程序就崩溃了,系统提示是浮点异常问题,其实这个浮点异常问题对应的就是我们的硬件异常,它对应的信号是8号信号SIGFPE

大致原理:在计算机内部是有一个状态寄存器的,该寄存器内部是一个位图结构,如果对应的比特位为1就表示本次计算有数据溢出的情况,说明本次计算结果不正确,CPU执行有误,而操作系统每次调度进程时都会去检查状态寄存器的状态,确保进程的执行的正确性。

当让CPU执行除0操作就会引发数据溢出的问题,然后状态寄存器里面对应的比特位被置为1,我们操作系统检测到了状态寄存器中有比特位被置为1,就会向对应的进程发送SIGFPE信号终止掉该进程,于是除0就会导致程序崩溃。

在这里插入图片描述

下面我们可以用信号捕捉去验证我们上面的原理和结论。

#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>

void handler(int sig)
{
    std::cout << "我是收到 " << sig <<"信号才崩溃了"<< std::endl;
}

int main()
{
    signal(SIGFPE, handler);
    int a = 10;
    a /= 0;
    std::cout << a << std::endl;
 
    return 0;
}

运行结果:

在这里插入图片描述

可以看到我们的程序出现了死循环的打印,这是因为我们捕捉了8号信号,将原来的默认动作终止进程修改成了打印动作,当我们的进程处理完信号时,操作系统再次调用该进程时,由于上一次的状态寄存器里面的比特位没有被置0,所以操作系统再次调用该进程时,看到的状态寄存器的对应比特位为还是1,于是又向该进程发送8号信号,而我们的自定义动作始终没有去处理状态寄存器,于是就陷入了死循环当中。

所以我们一般都是捕捉完该信号以后让该进程直接退出。


下面我们来看野指针引起的硬件异常:

#include <iostream>
#include <signal.h>
#include <unistd.h>

int main()
{
    int* p = nullptr;
    *p = 10;
    std::cout << "野指针问题" << std::endl;
    return 0;
}

运行结果:

在这里插入图片描述

系统提示我们发生了段错误,对于野指针问题,其实也是我们进程收到了操作系统发送的信号而崩溃的,这个信号是11号信号SIGSEGV,而这一次硬件异常的是MMU单元(内存管理单元)。

大致原理:由于我们进程使用的地址都是虚拟地址,当我们进程的代码实际被执行时,需要进行虚拟地址到物理地址的转换,而这个转换就要借助MMU这个硬件来进行转换,当我们的MMU在进行地址转换时,MMU单元在页表中寻找地址的映射关系并比较读写权限是否一致,如果在页表中找不到映射或者找到了映射但是进行的操作与读写权限不一致,就会导致转换失败,进而告知操作系统,操作系统识别以后就会向对应的进程发送SIGSEGV信号,从而终止掉该进程。(注意这里对于这个转换异常,操作系统并没有修复,如果用户捕捉了这个信号,也不修复也不退出,也会导致操作系统一直给该进程发送此信号)

对于0地址可能操作系统根本没有给0地址建立映射关系,或者建立了映射关系但是操作系统不会允许0地址处发生写入!而当我们进行*p = 10时,是需要进行写入的,MMU在地址转换时发现权限不一致,进而引发给异常,报告给了操作系统,然后操作系统向我们的的进场发送SIGSEGV信号。

在这里插入图片描述

结语

本章讲述的是进程信号的产生,但是只知道这些还是不够的,下一章我们继续深入理解进程信号的保存,提升我们对于信号的理解。

当然如果本篇文章有错误或不足的地方,欢迎评论或私信讨论!那么我们下期见,byebye!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/34614.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PS扣签名

打开Photoshop CS6&#xff0c;依次点击“文件”-“打开”&#xff0c;把签名照导入进来。 在“选择”菜单下点击“色彩范围”。 此时鼠标形状变成了一支笔&#xff0c;点击签名上黑色的地方&#xff0c;适当调整颜色容差&#xff0c;点击“确定”完成选择。 按住CtrlJ组…

1. Netty核心功能与线程模型详解

Netty 1. 认识Netty2. 第一个Netty程序3. Netty组件3.1 EventLoop和EventLoopGroupChannelChannelPipeline和ChannelHandlerContextChannelPipeline中ChannelHandlerChannelPipeline 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点…

开源免费的多数据库工具Chat2DB

Chat2DB v1.0.11使用 当前使用的版本为1.0.11&#xff0c;目前已经更新到2.0.0版本。 一.Chat2DB介绍 Chat2DB 是一款开源免费的多数据库客户端工具。 能够将自然语言转换为SQL&#xff0c;也可以将SQL转换为自然语言。 支持windows、mac本地安装&#xff0c;也支持服务器端…

哈工大计算机网络课程网络层协议详解之:Internet路由BGP协议详解

哈工大计算机网络课程网络层协议详解之&#xff1a;BGP协议详解 在之前的网络层协议中&#xff0c;我们介绍了Internet网络两个自治系统内的路由协议&#xff1a;RIP协议和OSPF协议。这两个协议应该来说是自治系统内协议的两个代表性协议&#xff0c;前一个基于距离向量路由算…

vue项目业务实现,视频监控-文件流,大屏适配方案(v-scale-screen),websocket前端

最近把以前的业务场景及解决方案整理了一下&#xff0c;具体实现的工具如下&#xff1a; 监控-视频文件流>video.js videojs-contrib-hls 大屏适配方案> v-scale-screen websocket>sockjs-client webstomp-client 视频监控-文件流 使用方法 下载video插件&#xf…

异步交互技术Ajax

Ajax 概念&#xff1a;Asynchronous JavaScr And XML 异步的JavaScript和XML作用&#xff1a; 数据交换&#xff1a;通过Ajax可以给服务器发送请求&#xff0c;并获取服务响应的数据异步交互&#xff1a;可以在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部…

第3章 创建项目并初始化业务数据(过程记录)

项目声明和依赖 ECommerceRecommendSystem [pom.xml] 公用的声明、依赖、插件 properties 声明 log4g&#xff1a;处理日志的框架&#xff08;日志的具体实现&#xff09;sel4g&#xff1a;简单日志门面&#xff08;简单日志的接口&#xff09;mongodb-spark&#xff1a;Mong…

用隐私换便利,,,,,,您配吗?

用隐私换便利,您配吗&#xff1f; 引言 近日&#xff0c;某高校毕业生在校期间窃取学校内网数据&#xff0c;收集全校学生个人隐私信息的新闻引发了人们对互联网生活中个人信息安全问题的再度关注。在大数据时代&#xff0c;算法分发带来了隐私侵犯&#xff0c;在享受消费生活…

TX Text Control .NET for WPF 31.SP3 Crack

.NET WPF 应用程序的文档处理 将文档编辑、创建和 PDF 生成添加到您的 WPF 应用程序中。 视窗用户界面 功能齐全的文档编辑器 TX Text Control 是一款免版税、完全可编程的丰富编辑控件&#xff0c;它在专为 Visual Studio 设计的可重用组件中为开发人员提供了广泛的文字处理功…

Matlab学习-轨迹热力图绘制

Matlab学习-轨迹热力图绘制 参考链接&#xff1a; MathWork-scatter函数使用 问题需求&#xff1a; 需要将轨迹上的点另一维信息同时显示在图上&#xff0c;比如横纵向误差等&#xff0c;这个时候画轨迹与误差的热力图就能很好同时反应位置和定位误差之间的关系&#xff1b;…

CSS圆角进化论

CSS圆角发展过程 大致经历了3个阶段&#xff0c;包括&#xff1a; 背景图片实现圆角CSS2.0标签模拟圆角CSS3.0圆角属性&#xff08;border-radius属性)实现圆角 ☛背景图片实现圆角&#xff1a;使用背景图片实现圆角的方式很多&#xff0c;实现的方式和圆角的切图方式关系密…

高级篇十六、多版本并发控制(重要)

目录 1、什么是MVCC2、快照读与当前读2.1 快照读2.2 当前读 3、复习3.1 隔离级别3.2 隐藏字段、Undo Log版本链 4、MVCC实现原理之ReadView4.1 什么是ReadView&#xff1f; 1、什么是MVCC MVCC &#xff08;Multiversion Concurrency Control&#xff09;&#xff0c;多版本并…

电脑出现0xC1900101错误怎么办?

在更新或安装Windows操作系统时&#xff0c;有时系统会提示出现了0xC1900101错误。这个错误的出现通常是源于与驱动程序相关的错误所致。那么当电脑出现0xC1900101错误时该怎么办呢&#xff1f; 为什么会出现错误代码0xC1900101&#xff1f; 通常情况下&#xff0c;有以下几个…

error: exportArchive: No signing certificate \“Mac Development\“ found

error: exportArchive: No signing certificate “Mac Development” found UNIAPP打包又遇到这个问题了, 证书过期续期的时候又遇到这个问题了(之前遇到过解决了,时间长忘了),记录一下,报错信息 error: exportArchive: No signing certificate \"Mac Development\"…

5.8.8 TCP流量控制

5.8.8 TCP流量控制 计算机网络的流量控制实际上是调节发送方的速率使得接收方能够及时处理的一个过程。 在TCP中采用的是大小可变的滑动窗口的方式进行流量控制&#xff0c;窗口大小的单位是字节。 如图 根据接收方的接收能力&#xff0c;通过接收窗口rwnd可以实现一个端到端…

C语言王国探险记之字符串+注释

王国探险记系列 文章目录&#xff08;3&#xff09; 前言 一&#xff0c;什么是字符串呢&#xff1f; 1&#xff0c;那C语言是怎么表示字符串的呢? "hello world.\n" 2&#xff0c;证明字符串的结束标志是一个 \0 的转义字符 3&#xff0c;证明字符串的结束标…

基于springboot+Redis的前后端分离项目(二)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 商户查询缓存&#xff0c;缓存更新策略&#xff0c;缓存穿透 商户查询缓存a.什么是缓存1.为什么要使用缓存2.如何使用缓存 b.添加商…

微信小程序分包

原生小程序分包和 uniapp 小程序分包差不多。 分包只是在原有代码结构上&#xff0c;指定那个文件夹属于分包&#xff0c;所以&#xff0c;页面地址原本路径不会修改。 比如&#xff0c;将pages/mine设为分包&#xff0c;mine下面的有页面地址pages/mine/index/index&#xf…

系统盘空间不足怎么清理?Win11系统盘瘦身的方法

系统盘空间不足怎么清理&#xff1f;当我们的电脑使用久了&#xff0c;就会有大量的垃圾文件堆积&#xff0c;会给系统盘空间带来很大的压力&#xff0c;容易出现系统运行卡顿的情况&#xff0c;对此我们需要对系统盘进行一次大扫除。本期教程小编将为大家分享Win11系统盘瘦身的…

20本期刊影响因子上涨,7月SCI/SSCI/AHCI/EI刊源表已更新!

2023年7月SCI/SSCI/AHCI/EI期刊目录更新 2023年6月28日发布的最新《期刊引证报告》中&#xff0c;我处合作期刊中&#xff0c;7月刊源表有20本期刊影响因子上涨&#xff0c;同时新增多本快刊&#xff01; 重磅&#xff01;2023年JCR正式发布&#xff08;附影响因子名单下载&a…