【Linux操作系统】信号的产生捕获

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️林 子
      🛰️博客专栏:✈️ Linux
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

目录

  • 信号是什么?
  • 信号是如何产生的?
    • 1.键盘输入
    • 2.程序(进程)异常
    • 3.系统调用
    • 4. 由软件条件来产生信号
  • 信号捕获
  • 总结:

信号是什么?

什么是信号? 信号是一种抽象概念,在我们的生活中就有各种各样的信号。 例如红绿灯,凌晨六点的鸡鸣声,你考试成绩不好时你爸妈的脸色。 这些都是信号。 而信号有一重要的特征就是,当这些信号出现时,你知道接下来会发生什么!然后采取相应的措施。

比如说:

红灯来了,你知道你不能过马路,所以你停止前进。

凌晨六点鸡叫了, 你知道已经天亮了,你可以起床,也可以继续睡觉,这都是处理信号的一种方式。

你考试成绩不好回家,你爸妈的脸色也可以告诉你,你爸妈现在心情很不开心,所以你知道你不能惹你爸妈生气。

以上都是现实中的信号,那么在我们的计算机程序中,也有信号!

而信号在产生之前,我们的程序就知道如何处理这个信号。

信号是如何产生的?

那么我们程序中的信号是如何产生的呢?

1.键盘输入

我们在键盘中有一些快捷键,比如 ctrl + c,会强制关掉当前前台运行的程序,本质是利用键盘输入,让OS向目标进程发送了2号信号。

我们可以写个死循环程序演示一下。

#include<iostream>int main()
{while(1){printf("hello linux\n");}return 0;
}

然后我们运行程序,按ctrl + c ,程序会被终止。实际上是产生了信号。

在这里插入图片描述

2.程序(进程)异常

当程序出现异常 , 例如 **空指针解引用,除0,等异常都会产生信号。 而本质是程序在执行中,发生了硬件错误。空指针解引用会发生内存错误, 除0会发生cpu计算错误。 所以就会产生信号,再由OS来处理这些信号。**这种情况就不演示了,因为这种异常在学习C语言的过程中想必大家都遇见过。

3.系统调用

我们可以用一些系统调用接口来产生信号,例如 我们命令行上的 kill 命令 。 本质就是系统调用int kill(pid_t pid, int signo) 函数来产生信号。还有 int raise(int signo) 函数来自己产生信号, 还有 void abort(void) 来产生 3号信号。

**int kill(pid_t pid, int signo) 演示 **

这个函数的第一个参数是一个进程的pid ,第二个参数是发送的参数,类似于我们 kill命令行。所以我们需要以命令行的方式 获取被发送信号进程的pid。

我们写一个kill 函数,参数用我们命令传进去的 argv[1] 和 argv[2]

#include<stdio.h>
#include<signal.h>
#include<stdlib.h>int main(int argc , char* argv[])
{if(argc != 3){printf("./mykill pid signal");return 1;}kill(atoi(argv[1]),atoi(argv[2]));return 0;
}

然后我们执行一个进程,每秒打印一次 hello linux。然后用 mykill 对 mypro 发送9号信号!

在这里插入图片描述

这样我们就模拟实现了一个 kill 命令。本质就算调用了系统调用接口 kill。

int raise(int signo) 演示

我们让程序打印5次 hello linux 后调用这个函数,并传参数9进去。看看进程会不会收到9号信号。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>int main()
{int count = 0;while(1){printf("hello linux\n");sleep(1);count ++;if(count == 5){raise(9);}}return 0;
}

然后我们运行这个程序

在这里插入图片描述

不难发现,五秒过后收到了9号信号,这就是自己产生信号的一种方式。

4. 由软件条件来产生信号

SIGPIPE是一种由软件条件产生的信号, 当我们在管道传输中,关闭了读端,而写端还在继续写入。那么就会产生SIGPIPE信号。

还有 alarm函数 产生 SIGALRM信号。

SIGPIPE信号暂且不演示,我们演示一下 alarm函数。

alarm函数是一个闹钟函数,参数传一个秒数,表示定时多少秒,时间到了之后则会收到alarm信号。返回值则是剩余的秒数,就是当闹钟被中断时。返回剩余的秒数,参数为0时则终端闹钟。

unsigned int alarm(unsigned int seconds)

我们先测试一下该函数。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>int main()
{alarm(5);while(1){printf("hello linux1\n");printf("hello linux2\n");}return 0;
}

运行程序

在这里插入图片描述

我们发现五秒后收到信号。

那么我们取消闹钟试试

#include<stdio.h>
#include<unistd.h>
#include<signal.h>int main()
{alarm(30); //注册闹钟int count = 0;while(1){printf("hello linux1\n");printf("hello linux2\n");sleep(1);count++;if(count == 5){int ret = alarm(0); //取消闹钟printf("ret = %d\n",ret); //打印剩余秒数}}return 0;
}

然后我们执行。

在这里插入图片描述

5s后闹钟取消了,返回了剩余的秒数,25秒。

信号捕获

信号的捕获函数:

#include <signal.h> //头文件

typedef void (*sighandler_t)(int); // 对函数指针typedef

sighandler_t signal(int signum, sighandler_t handler); //函数

signal函数的作用就是,如果接收到 signum , 那么就用handler函数去处理该信号。

举个例子:

我们执行下面代码,然后尝试用 kill命令对该进程发送2号信号。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>//处理信号函数
void handler(int sign)
{printf("get a sign :  %d\n", sign);exit(0);
}int main()
{signal(2,handler); //如果收到二号信号,那么用handler来处理while(1){printf("hello world\n");sleep(1);}return 0;
}

然后我们测试一下。

在这里插入图片描述

我们发现最后输出了 get a sign 2 。 说明信号被捕捉了。

我们用 kill -l 可以查看系统中的信号。

在这里插入图片描述

我们只看前面31个,那么我们可以把前面31个都用 handler 处理。然后进行一个 除0 操作,会收到几号信号。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>//处理信号函数
void handler(int sign)
{printf("get a sign :  %d\n", sign);exit(0);
}int main()
{for(int i = 1;  i<= 31 ; i++)signal(i,handler); //如果收到二号信号,那么用handler来处理sleep(5) ; int a = 5 / 0;  //5秒之后制造一个信号return 0;
}

然后我们运行一下。

在这里插入图片描述

5 秒过后,我们程序没有报错,相反是打印了 收到 8号信号的信息。而 8 号对应的就是SIGFPE信号,浮点数错误。在这里插入图片描述

现在知道信号可以捕获,那么问题来了, 9 号信号能否捕获呢? 我们可以用下面代码试一下。

我们写了一个死循环代码,然后用命令行发送9号信号,看看是否会被捕获。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>//处理信号函数
void handler(int sign)
{printf("get a sign :  %d\n", sign);exit(0);
}int main()
{for(int i = 1;  i<= 31 ; i++)signal(i,handler); //如果收到二号信号,那么用handler来处理while(1){sleep(1);printf("hello world\n");}return 0;
}

我们试验一下。

在这里插入图片描述

最后我们发现, 9 号信号没有被捕获,进程还是被杀死了。

所以得出结论: 9号信号无法被捕获,操作系统不允许有金刚不坏的进程存在!

总结:

信号的四种产生方式:

1.终端键盘输入

2.进程异常

3.系统调用

4.软件条件产生

信号捕获函数:

sighandler_t signal(int signum, sighandler_t handler),其中 handler 是一个函数指针类型

特殊信号:

9号信号无法被捕获!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习计算机网络中的一些疑问及解答

文章目录 前言一、为什么要进行三次握手二、三次握手的流程三、三次握手中seq和ack的值四、四次挥手流程五、四次挥手中seq和ack的值六、为什么要等待才回复七、为什么等待2MSL总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;在学习计算机网络的过程中遇…

780. 到达终点;2360. 图中的最长环;1871. 跳跃游戏 VII

780. 到达终点 核心思想&#xff1a;正难则反&#xff0c;如果从起点到终点很难想。那么我们就考虑从终点到起点&#xff0c;由于起点为正数&#xff0c;那么终点&#xff08;x,y&#xff09;的上一步一定是&#xff08;x-y,y&#xff09;或者(x,y-x)很明显肯定是大值减去小的…

Vue 2 组件间的通信方式总结

引言 组件间的关系有父子关系、兄弟关系、祖孙关系和远亲关系。 不同的关系间&#xff0c;组件的通信有不同的方式。 一、prop 和 $emit prop向下传递&#xff0c;emit向上传递。 父组件使用 prop 向子组件传递信息。 ParentComponent.vue <template><div><…

5-2 Pytorch中的模型层layers

深度学习模型一般由各种模型层组合而成。 torch.nn中内置了非常丰富的各种模型层。它们都属于nn.Module的子类&#xff0c;具备参数管理功能。 例如&#xff1a; nn.Linear, nn.Flatten, nn.Dropout, nn.BatchNorm2d, nn.Embedding nn.Conv2d,nn.AvgPool2d,nn.Conv1d,nn.ConvTr…

用AI在小红书做早教启蒙,2个月涨粉11.7万,获赞10万的新流量玩法

本期是赤辰第29期AI项目教程&#xff0c;底部准备了9月粉丝福利&#xff0c;可以免费领取。母婴、教育一直以来是最不缺流量的两大“真香”赛道。那么AI时代下有怎样新流量红利和玩法&#xff1f;接下来就给大家拆解一个在小红书上做AI绘画启蒙早教资源变现的新玩法&#xff01…

Dajngo06_Template模板

Dajngo06_Template模板 6.1 Template模板概述 模板引擎是一种可以让开发者把服务端数据填充到html网页中完成渲染效果的技术 静态网页&#xff1a;页面上的数据都是写死的&#xff0c;万年不变 动态网页&#xff1a;页面上的数据是从后端动态获取的&#xff08;后端获取数据库…

每日一博 - 防范彩虹表攻击_数据库存储密码的秘密武器

文章目录 概述图解小结 概述 加盐&#xff08;salting&#xff09;是一种安全存储数据库中密码并验证其真实性的常见方法&#xff0c;它的主要目的是增加密码的安全性&#xff0c;以防止常见的密码攻击&#xff0c;如彩虹表攻击。以下是关于如何使用加盐技术的简要介绍&#x…

Eclipse开源代码下载

当前插件开发&#xff0c;需要修改eclipse源码&#xff0c;如需要修改remote相关的代码&#xff0c;所以需要下载相关源码。网上大多资料都说的不清不楚的&#xff0c;也可能我太小白&#xff0c;不明白&#xff0c;反正就是折腾了一两天才感觉有点思路&#xff0c;改如何找源码…

信息安全三级真题一

目录 一、单选题 二、填空题 三、综合题 一、单选题 二、填空题 三、综合题 知法懂法&#xff0c;请各位网络安全从业者遵守《网络安全法》、《个人信息保护法》 业%$务*$&联&#系 XHU3ZjUxXHU3ZWRjXHU4ZmQwXHU3ZWY0XHU2ZTE3XHU5MDBmXHU1NmUyXHU5NjFmXHUyMDBiXHU2M…

安卓抓jdwskey

安装京东&#xff0c;VNET VNET下载地址 https://www.vnet-tech.com/zh/ 2给权限 打开 VNET --点击右下角 ▶ --保存 CA.pem 证书 --打开手机系统设置搜索 证书–点击安装刚刚保存的 CA.pem 如果开始出现数据表示已经有权限抓包了&#xff0c;下面给权限跳过&#xff0c;直接开…

【PowerQuery】Excel的PowerQuery按需刷新

将数据通过PowerQuery 导入进来后,这里将进行数据分组运算,最终的数据计算结果将保存在Excel 表格中,图为销售统计结果。 在Excel中,如果我们希望进行销售统计的手动更新可以使用几种不同的方法来进行刷新: 刷新单一数据连接如果仅仅需要刷新单一数据连接的话我们可以通过…

MyBatis获取参数值的两种方式

5、MyBatis获取参数值的两种方式 MyBatis获取参数值的两种方式&#xff1a;KaTeX parse error: Expected EOF, got # at position 4: {}和#̲{} 5.1、单个字面量类型的…{}和#{}以任意的名称获取参数的值&#xff0c;注意${}需要手动加单引号 ${} #{} 测试代码&#xff1a; 实验…

CocosCreator3.8研究笔记(十五)CocosCreator 资源管理Asset Bundle

在资源管理模块中有一个很重要的功能&#xff1a; Asset Bundle&#xff0c;那什么是Asset Bundle &#xff1f;有什么作用&#xff1f;怎么使用 Asset Bundle呢 &#xff1f; 一、什么是Asset Bundle &#xff1f;有什么作用&#xff1f; 在日常游戏开发过程中&#xff0c;为了…

TCP详解之滑动窗口

TCP详解之滑动窗口 引入窗口概念的原因 我们都知道 TCP 是每发送一个数据&#xff0c;都要进行一次确认应答。当上一个数据包收到了应答了&#xff0c; 再发送下一个。 这个模式就有点像我和你面对面聊天&#xff0c;你一句我一句。但这种方式的缺点是效率比较低的。 如果你…

TS泛型的使用

函数中使用泛型&#xff1a; function identity<T>(arg: T): T {return arg; }let result identity<number>(10); // 传入number类型&#xff0c;返回number类型 console.log(result); // 输出: 10let value identity<string>(Hello); // 传入string类型&a…

TCP详解之重传机制

TCP详解之重传机制 TCP 实现可靠传输的方式之一&#xff0c;是通过序列号与确认应答。 在 TCP 中&#xff0c;当发送端的数据到达接收主机时&#xff0c;接收端主机会返回一个确认应答消息&#xff0c;表示已收到消息。 但在错综复杂的网络&#xff0c;并不一定能如上图那么顺…

阿里云无影云电脑详细介绍_无影优势价格和使用

什么是阿里云无影云电脑&#xff1f;无影云电脑&#xff08;原云桌面&#xff09;是一种快速构建、高效管理桌面办公环境&#xff0c;无影云电脑可用于远程办公、多分支机构、安全OA、短期使用、专业制图等使用场景&#xff0c;阿里云百科分享无影云桌面的详细介绍、租用价格、…

【CentOS7】vsftpd学习笔记

2023年9月14日&#xff0c;周四下午 目录 安装vsftpd添加账号给账户设置新密码开放21号端口关闭21号端口查看vsftpd的运行状态启动vsftpd关闭vsftpd查看CentOS7的IP地址在Windows测试你的运行在CentOS7的用vftpd构建的FTP服务器查看Windows自带的ftp程序有哪些可用的命令 安装…

【C++】list的模拟实现【完整理解版】

目录 一、list的概念引入 1、vector与list的对比 2、关于struct和class的使用 3、list的迭代器失效问题 二、list的模拟实现 1、list三个基本函数类 2、list的结点类的实现 3、list的迭代器类的实现 3.1 基本框架 3.2构造函数 3.3 operator* 3.4 operator-> 3…