【Linux】信号的产生

server/2024/10/18 16:54:11/

目录

    • 一. 信号的概念
      • signal() 函数
    • 二. 信号的产生
      • 1. 键盘发送
      • 2. 系统调用
        • kill()
        • raise()
        • abort()
      • 3. 软件条件
        • alarm()
      • 4. 硬件异常
        • 除零错误:
        • 野指针:
    • 三. 核心转储

一. 信号的概念

  • 信号是消息的载体, 标志着不同的行为; 是进程间发送异步信息的一种方式, 属于软中断.

  • 信号随时都可能产生, 是异步发送的, 所以进程并不会立即处理信号, 进程会在合适的时间进行统一处理; 所以进程必须有保存信号的能力;

  • 信号是以位图的形式保存在 task_struct 的 pending 中, 当进程接受到信号时, 进程会将信号对应的位置设置为 1 即可;

struct task_struct {//...struct signal_struct *signal;	// 指向进程信号描述符struct sighand_struct *sighand;	// 指向进程信号处理程序描述符sigset_t blocked;  				// 进程阻塞的信号, 信号按照比特位相对应struct sigpending pending;		// 进程的待处理信号//...
};
  • 信号的处理方式分为三种:
    默认动作
    自定义动作;
    忽略.

在 Linux 中可以使用指令查看信号列表

kill -l

在这里插入图片描述
一共 62 个信号, 其中 1~31 号信号为普通信号, 适用于基于时间片的分时操作系统; 34~64 号信号为实时信号, 适用于实时操作系统.

使用指令可以查看普通信号的说明

man 7 signal

在这里插入图片描述

signal() 函数

在这里插入图片描述

  • signal() 函数, 可以将 signum 指定信号的处理动作, 设置为 handler 动作; 但 SIGKILL 和 SIGSTOP 不能被捕捉或忽略.
#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);

参数:

  • signum: 信号编号, 设置的信号;
  • handler: 函数指针, 是信号的处理动作;
    若 handler 设置为 SIG_IGN, 那么设置的信号将会被忽略;
    若设置为 SIG_DFL, 那么信号将会执行默认动作;
    若设置为 自定义函数, 那么处理信号时, 将会调用自定义函数

返回值:

  • 若成功, 返回 signal handler 的前一个值; 若失败, 返回 SIG_ERR, 并且设置 erron.
    在这里插入图片描述

二. 信号的产生

1. 键盘发送

当操作系统从键盘中读取 “ctrl” + “c” 类似的特定的组合键时, 会将其解释为某种信号, 发送给前台进程.

  • 例:
    一个死循环的前台进程可以被 “ctrl” + “c” 终止,
#include <iostream>
#include <signal.h>using namespace std;int main()
{cout << endl;while (1) ;return 0;
}

在这里插入图片描述

将 2 号信号注册自定义行动 验证

#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void handler(int signal)
{cout << endl << getpid() << " " << signal << endl;
}int main()
{signal(2, handler);while (1) ;return 0;
}

在这里插入图片描述

2. 系统调用

kill()

在这里插入图片描述

  • kill() 函数, 发送指定信号至指定进程.
#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);

参数:

  • pid: 指定的进程 pid;
  • sig: 指定的信号编号;

返回值:

  • 若成功, 返回 0; 若失败, 返回 -1, 并且设置 errno.
    在这里插入图片描述
raise()

在这里插入图片描述

  • raise() 函数, 发送指定信号至当前进程.
#include <signal.h>int raise(int sig);

参数:

  • sig: 指定的信号编号;

返回值:

  • 若成功, 返回 0; 若失败, 返回 非0, 并且设置 errno.
    在这里插入图片描述
abort()

在这里插入图片描述

  • abort() 函数, 发送 SIGABRT 信号至当前进程, 该函数从不返回.
#include <stdlib.h>void abort(void);

3. 软件条件

这种方式在管道就出现了:
管道读写时, 若读端关闭, 写端继续写入时, 那么操作系统将会发送 SIGPIPE 信号终止写端.

alarm()

在这里插入图片描述

  • alarm() 函数, 将会在指定时间后, 发送 SIGALRM 信号值当前进程.
#include <unistd.h>unsigned int alarm(unsigned int seconds);

参数:

  • seconds: 指定时间, 单位为秒;

返回值:

  • 若前一次 alarm() 函数没有剩余的时间, 返回 0; 若前一次 alarm() 函数有剩余的时间, 返回剩余的时间.
    在这里插入图片描述

4. 硬件异常

硬件异常是指 硬件在检测到错误条件后, 通知内核, 再由内核发送相应信号至相关进程.

除零错误:

当 CPU 发生除零错误后, CPU 会将内部的标志寄存器设置为 1, 表示出现数据异常; 之后通知操作系统, 由操作系统向进程发送 SIGFPE 信号, 该信号默认动作为终止程序;
但若将 SIGFPE 信号设置自定义动作或忽略, 未处理异常时, 该硬件的异常数据属于进程的上下文; 由于进程依旧运行, 并且状态寄存器依旧为异常状态, 那么操作系统会轮询式的发送异常信号至进程, 直至异常被处理或终止.

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>using namespace std;void handler(int signal)
{cout << getpid() << " " << signal << endl;sleep(1);
}int main()
{signal(8, handler);int n = 1;n /= 0;return 0;
}

在这里插入图片描述
在这里插入图片描述

野指针:

指针异常通常有两种: 无当前虚拟地址的映射关系 或 虚拟地址的操作和页表中的权限不匹配;
当 CPU 的 MMU 寄存器转换地址异常时后, CPU 会将异常的虚拟地址保存至 CR2 寄存器中, 状态设置为异常状态; 之后通知操作系统, 由操作系统向进程发送 SIGSEGV 信号, 该信号默认动作为终止程序;
但若将 SIGSEGV 信号设置自定义动作或忽略, 未处理异常时, 该硬件的异常数据属于进程的上下文; 由于进程依旧运行, 并且 CR2 寄存器依旧为异常状态, 那么操作系统会轮询式的发送异常信号至进程, 直至异常被处理或终止.

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>using namespace std;void handler(int signal)
{cout << getpid() << " " << signal << endl;sleep(1);
}int main()
{signal(11, handler);char* ptr = nullptr;*ptr = 0;return 0;
}

在这里插入图片描述
在这里插入图片描述

三. 核心转储

core dump(核心转储), 当进程出现异常终止时, 操作系统会将该进程的相关数据保存至当前目录下的一个核心转储文件, 文件名通常为 core 或 core.pid;

核心转储文件的主要目的是为了调试程序, 并且可以直接从出错的地方开始调试, 也叫做事后调试;

当信号的处理动作为 Core 时, 操作系统不仅终止程序, 还会创建核心转储文件; 但由于核心转储文件可能会包含敏感信息, 或文件过大等原因, 此功能通常是关闭的.

例:

  • 核心转储的打开和关闭
    使用指令可以查看当前系统中的资源限制情况, 默认核心转储文件大小为 0;
ulimit -a 

在这里插入图片描述
使用指令可以自行设置核心转储文件大小, 单位为 blocks

ulimit -c /*file_size*/

将核心转储文件大小设为 0, 即可关闭核心转储功能

ulimit -c 0
  • 核心转储的使用
g++ file_name -g 		// 编译文件时使用选项生成可调试文件// 运行程序, 生成核心转储文件
gdb file_name  			// 进入调试模式
core-file corefile_name // 使用核心转储文件, 直接定位至出错的地方
  • 父进程也可以通过等待查看子进程是否创建了核心转储文件; 若生成, core dump 标志设为 1, 若没有生成, core dump 标志为 0;
    在这里插入图片描述

http://www.ppmy.cn/server/14155.html

相关文章

Java面试之数据类型

在Java中&#xff0c;数据类型用于声明变量并指定变量能够存储的数据类型。Java的数据类型可以分为两大类&#xff1a;基本数据类型和引用数据类型。 一、基本数据类型&#xff1a; 1.1、整型&#xff1a;用于表示整数值&#xff0c;包括byte、short、int和long。 byte&…

线上线下交友社区系统 可打包小程序 支持二开 源码交付!

社交网络的普及&#xff0c;人们交友的方式发生了巨大的变化。过去&#xff0c;我们主要通过线下的方式来结识新朋友&#xff0c;比如在学校、工作场所、社交活动或者兴趣小组中。然而&#xff0c;随着移动端软件的发展&#xff0c;线上交友也逐渐变得流行。 方便性&#xff1a…

牛客 题解

文章目录 day4_17**BC149** **简写单词**思路&#xff1a;模拟代码&#xff1a; dd爱框框思路&#xff1a;滑动窗口&#xff08;同向双指针&#xff09;代码&#xff1a; 除2&#xff01;思路&#xff1a;模拟贪心堆代码&#xff1a; day4_17 BC149 简写单词 https://www.now…

指针(5)

前言 本节是有关指针内容的最后一节&#xff0c;本节的内容以讲解指针习题为主&#xff0c;那么就让我们一起来开启本节的学习吧&#xff01; sizeof和strlen的对比 1.sizeof 我们在学习操作符的时候&#xff0c;学习了sizeof。sizeof存在的意义是用来计算变量所占用的内存空…

(救命)Kali Linux或者其他linux系统的触控板右键按下没反应,失效的解决办法

我每次安装kali的时候都会选择gnome桌面&#xff0c;每次安装好右键都是禁用的&#xff0c;按下和左键效果一样&#xff0c;每次都得去调鼠标右键&#xff0c;原来就不好找到那个选项&#xff0c;这次踏马居然连那个选项都没了&#xff0c;如果你去网上找教程你会发现网上根本没…

神经网络与深度学习(四)

目录 一、循环神经网络1.1 门控循环单元(GRU)1.2 长短期记忆网络(LSTM)1.3 深度循环神经网络1.4 双向循环神经网络 二、NLP2.1 序列模型2.2 数据预处理2.3 文本预处理2.4 文本嵌入 三、RNN模型3.1 RNN概要3.2 RNN模型3.3 RNN示例 一、循环神经网络 循环神经网络&#xff08;RN…

猎聘爬虫(附源码)

废话不多说直接附源码 cookies需要替换成自己的 , 该网站在不登录的情况下只能请求到10页数据 , 想要获得完整数据需要携带登录后的cookies import requests import json from lxml import etree import os import openpyxlheaders {"Accept": "application/…

远程仓库.github/workflow的 yml如何配置

git 远程仓库.github/workflow的 yml如何配置 关于远程仓库 GitHub 的协作开发方法取决于将本地存储库中的提交发布到 GitHub 以便其他人查看、获取和更新。 远程 URL 是 Git 表达“代码存储位置”的奇特方式。该 URL 可以是您在 GitHub 上的存储库&#xff0c;也可以是其他用…