【Linux】信号

目录

一、信号的概念

[1. 什么是信号](#1. 什么是信号)

[2. 信号的种类](#2. 信号的种类)

二、信号的产生

[1. kill命令](#1. kill命令)

[2. 硬件产生](#2. 硬件产生)

[3. 系统调用](#3. 系统调用)

[4. 软件条件](#4. 软件条件)

[5. 程序异常](#5. 程序异常)

三、信号的捕捉与保存

[1. 信号的捕捉](#1. 信号的捕捉)

[2. 信号的保存](#2. 信号的保存)

四、信号的执行

[1. 默认动作](#1. 默认动作)

[2. 忽略动作](#2. 忽略动作)

[3. 自定义动作](#3. 自定义动作)


**一、**信号的概念

1. 什么是信号

从一个故事理解信号

  • 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能"识别快递"
  • 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成"在合适的时候去取"。
  • 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你"记住了有一个快递要去取" 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:1. 执行默认动 作(幸福的打开快递,使用商品)2. 执行自定义动作(快递是零食,你要送给你你的女朋友)3. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)
  • 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话

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

  • 输入命令,在Shell下启动一个前台进程。
  • 用户按下Ctrl-C,键盘输入产生一个硬件中断。
  • 如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行, CPU从用户态切换到内核态处理硬件中断。
  • 终端驱动程序将Ctrl-C解释成一个SIGINT信号,记在该进程的PCB中(也可以说发送了一个SIGINT信号给该进程)。
  • 当某个时刻要从内核返回到该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行。

2. 信号的种类

使用命令查看:kill -l

本文我们只探究1-31号信号。

1-31号信号的默认动作大部分都为终止进程。

二、信号的产生

1. kill命令

可以通过kill命令向指定进程发送指定信号。

如向pid为12345的命令发送9号命令SIGKILL:

复制代码
kill -9 12345

2. 硬件产生

如在键盘上按ctrl + c,相当于向进程发送2号信号SIGINT。

3. 系统调用

kill函数

复制代码
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig); //用法参考kill命令

abort函数

复制代码
#include <stdlib.h>

void abort(void); //向系统发生6号信号SIGABRT

4. 软件条件

管道

但管道读端关闭时,os会向管道发送13号信号SIGPIPE,将管道销毁。

alarm函数

复制代码
#include <unistd.h>

unsigned int alarm(unsigned int seconds);
//相当与一个闹钟,当alarm函数触发后,经seconds秒后os向系统发送14号信号SIGALRM

5. 程序异常

当程序出现异常时,os会向程序发送8号信号SIGFPE或13号信号SIGEGV,导致程序退出。

我们看到的表现形式为程序奔溃。

三、信号的捕捉与保存

1. 信号的捕捉

2. 信号的保存

进程的PCB中有一个专门用来保存信号的字段。信号保存的结构由block、pending两个位图和handler这一个函数指针数组三部分组成。

信号的编号本质就是数组下标。

当信号产生时,对应pending位便会由0置1。os定期扫描pending位图,如果扫描时发现有pending位为1,且block位不为1,责执行该位的handler方法。

block位图是记录信号阻塞的位图。一旦某一信号的对应block位为1,该信号就被阻塞,及收到信号后只进行记录而不执行。

handler数组记录的是信号动作。

信号集操作函数如下:

复制代码
#include <signal.h>

int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo); 
  • sigset_t是一种位图数据结构
  • 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有 效信号。
  • 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系 统支持的所有信号。
  • 注意,在使用sigset_ t类型的变量之前,一定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的 状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号

四、信号的执行

信号有三种动作,默认、忽略和自定义,详细介绍如下:

1. 默认动作

即执行信号的默认功能。

2. 忽略动作

忽略该信号,即收到信号后pending位置0但不执行任何函数。

复制代码
#include <signal.h>

sighandler_t signal(int signum, SIG_IGN); 
//signum为想忽略的信号
//如:signal(SIGINT, SIG_IGN); 

3. 自定义动作

收到信号后执行用户自定义的方法。

复制代码
#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);
//signum为想忽略的信号,handler为函数指针
//如:

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

void test(int signo)
{
    std::cout << "test sig, signo : " << signo << std::endl;
}

int main()
{
    signal(SIGINT, test);
    while(1);
    return 0;
}
相关推荐
讯捷蓝达1 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
且从容.2 小时前
ubuntu安装flink
linux·ubuntu·flink
数字新视界3 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落3 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨3 小时前
WSL2的安装与使用
linux·wsl
微小的鱼4 小时前
Linux的渲染与显示系统链路
android·java·linux
lisanmengmeng4 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮5 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
上火的金鱼妹5 小时前
k8s生产实际问题排查(网关,数据库读写等)
运维·服务器
孙克旭_5 小时前
k8s基础组件与创建资源
运维·docker·容器·kubernetes