【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.mzph.cn/news/81124.shtml

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

相关文章

The driver has not received any packets from the server

在测试数据迁移时遇到的错误。 目录 一、错误 二、解决 三、数据迁移测试 3.1 环境 3.2 源码及测试 3.2.1 源码 3.2.2 测试结果&#xff08;太慢&#xff09; 3.2.3 源码修改 3.2.4 异常及解决 一、错误 The driver has not received any packets from the server. 二…

数学建模——微分方程介绍

一、基础知识 1、一阶微分方程 称为一阶微分方程。y(x0)y0为定解条件。 其常规求解方法&#xff1a; &#xff08;1&#xff09;变量分离 再两边积分就可以求出通解。 &#xff08;2&#xff09;一阶线性求解公式 通解公式&#xff1a; 有些一阶微分方程需要通过整体代换…

四种常用的自动化测试框架

一直想仔细研究框架&#xff0c;写个流水账似的测试程序不难&#xff0c;写个低维护成本的测试框架就很难了&#xff0c;所以研究多种测试框架还是很有必要的&#xff0c;知道孰优孰劣&#xff0c;才能在开始编写框架的时候打好基础&#xff0c;今天读到了KiKi Zhao的翻译文章&…

Java实现Ip地址获取

Java实现Ip地址获取 一、两种实现方式二、测试结果 一、两种实现方式 package com.lyp;import org.apache.commons.lang3.ObjectUtils;import java.net.*; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Optional;/***…

Linux Ubuntu20.04深度学习环境快速配置命令记录

一、驱动安装 1、更新系统包 sudo apt-get updatesudo apt-get upgrade 2、安装显卡驱动 使用apt方式安装驱动&#xff0c;多数情况不容易成功&#xff0c; 使用一下方法更佳&#xff1a; 1.查看合适显卡的驱动版本 ubuntu-drivers devices NVIDIA GeForce 驱动程序 - …

git压缩仓库

git 压缩仓库 git gc命令压缩增量存储单元,节省磁盘空间 du -sh 查看当前文件夹占用多少K 快照的存储: 对于修改的内容,做快照处理并保存. 对于未修改的文件,做引用处理.

SOLIDWORKS Composer位置关键帧的使用

SOLIDWORKS Composer是专业的SOLIDWORKS及3D文件处理的动画制作软件&#xff0c;作为SOLIDWORKS 产品线下的一个明星存在。 SOLIDWORKS Composer几乎可以处理任何SOLIDWORKS的模型文件并将之转化成可以动作的机械动画&#xff0c;可以引用在企业的网站、产品说明书以及工作指导…

MySQL 面试题——MySQL 基础

目录 1.什么是 MySQL&#xff1f;有什么优点&#xff1f;2.MySQL 中的 DDL 与 DML 是分别指什么&#xff1f;3.✨数据类型 varchar 与 char 有什么区别&#xff1f;4.数据类型 BLOB 与 TEXT 有什么区别&#xff1f;5.DATETIME 和 TIMESTAMP 的异同&#xff1f;6.✨MySQL 中 IN …

STM32F4X SPI W25Q128

STM32F4X SPI W25Q128 什么是SPISPI的特点SPI通信引脚SPI接线方式SPI速率SPI通信方式SPI时钟相位和时钟极性 STM32F4X SPISTM32F4X SPI配置STM32F4X SPI频率 W25Q128W25Q128存储结构W25Q128读写操作W25Q128常用指令读取ID命令(0x90)写使能命令(0x06)禁止写使能命令(0x04)读取W2…

黑马JVM总结(七)

&#xff08;1&#xff09;StringTable_编译器优化 “a”“b”对应#4&#xff1a;是去常量池中找ab的这个符号 astore 5&#xff1a;是把这个存入编号为5的局部变量 “ab”对应的指令 #4&#xff0c;跟“a”“b”对应#4下面弄是一样的 在执行s3“ab”这行个代码时&#xf…

在PHP8中向数组添加元素-PHP8知识详解

在php8中向数组添加元素有多种方法&#xff0c;在这里主要讲解几个常用的方法&#xff1a;使用方括号[]添加元素、使用array_unshift()函数&#xff0c;向数组的头部添加元素、使用array_push()函数&#xff0c;向数组的尾部添加元素、使用array_splice()函数添加元素。 1、使用…

学习Bootstrap 5的第十三天

目录 提示框 如何创建提示框 实例 指定提示框的位置 实例 弹出框 如何创建弹出框 实例 指定弹出框的位置 实例 关闭弹出框 实例 提示框 提示框是一个小小的弹窗&#xff0c;在鼠标移动到元素上显示&#xff0c;鼠标移到元素外就消失。 如何创建提示框 Bootstrap…

LeetCode 332. Reconstruct Itinerary【欧拉回路,通路,DFS】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

Vue3+ElementUI使用

<!DOCTYPE html> <html> <head><meta charset"UTF-8"><meta name"viewport" content"initial-scale1.0,maximum-scale1.0,minimum-scale1.0,user-scalable0, widthdevice-width"/><!-- 引入样式 --><lin…

【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…

bug总结问题集和知识点集(一)

目录 一 bug问题集1. 端口被占用 二 oracle1. oracle查看版本怎么操作2. oracle数据库&#xff1a;参数个数无效![在这里插入图片描述](https://img-blog.csdnimg.cn/6a2eebc164f9406c81525371893bbd11.png)3. ORACLE数据库如何完整卸载? 三 mybatis1. mybatis用注解如何实现模…

学习Node js:raw-body模块源码解析

raw-body是什么 raw-body的主要功能是处理HTTP请求体的原始数据。它提供了以下核心功能&#xff1a; 解析请求体&#xff1a;可以从HTTP请求中提取原始数据&#xff0c;包括文本和二进制数据。配置选项&#xff1a;通过配置项&#xff0c;可以设置请求体的大小限制、编码方式…

【LeetCode-简单题KMP】232. 用栈实现队列

文章目录 题目方法一&#xff1a;用输入栈和输出栈模拟队列 题目 方法一&#xff1a;用输入栈和输出栈模拟队列 只有输出栈为空的时候才能将输入栈的元素补充到输出栈&#xff0c;否则输出栈不为空&#xff0c;如果再从输入栈往输出栈填充元素&#xff0c;就会弄乱队列的先进先…

【SpringMVC】拦截器JSR303的使用

【SpringMVC】拦截器&JSR303的使用 1.1 什么是JSR3031.2 为什么使用JSR3031.3 常用注解1.4 Validated与Valid区别1.5 JSR快速入门1.5.2 配置校验规则# 1.5.3 入门案例二、拦截器2.1 什么是拦截器2.2 拦截器与过滤器2.3 应用场景2.4 拦截器快速入门2.5.拦截器链2.6登录案列权…

接口测试——接口协议抓包分析与mock_L1

目录&#xff1a; 接口测试价值与体系常见的接口协议接口测试用例设计postman基础使用postman实战练习 1.接口测试价值与体系 接口测试概念 接口&#xff1a;不同的系统之间相互连接的部分&#xff0c;是一个传递数据的通道接口测试&#xff1a;检查数据的交换、传递和控制…