【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;
}
相关推荐
cuisidong199722 分钟前
如何在 Kali Linux 上安装 Google Chrome 浏览器
linux·运维·chrome
凌云行者29 分钟前
使用rust写一个Web服务器——单线程版本
服务器·前端·rust
光通信学徒1 小时前
ubuntu图形界面右上角网络图标找回解决办法
linux·服务器·ubuntu·信息与通信·模块测试
wusam1 小时前
螺蛳壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习03(网络及IP规划)
运维·服务器·网络·docker·容器
你会发光哎u1 小时前
Webpack模式-Resolve-本地服务器
服务器·前端·webpack
南种北李1 小时前
Linux自动化构建工具Make/Makefile
linux·运维·自动化
一直在进步的派大星1 小时前
Docker 从安装到实战
java·运维·docker·微服务·容器
小飞猪Jay1 小时前
面试速通宝典——10
linux·服务器·c++·面试
哲伦贼稳妥2 小时前
一天认识一个硬件之电源
运维·其他·电脑·硬件工程
暗恋 懒羊羊2 小时前
Linux 生产者消费者模型
linux·开发语言·ubuntu