【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;
}
相关推荐
yaoxin52112326 分钟前
第十二章 I 开头的术语
运维·服务器
ProgramHan28 分钟前
1992-2025年中国计算机发展状况:服务器、电脑端与移动端的演进
运维·服务器·电脑
马立杰4 小时前
H3CNE-33-BGP
运维·网络·h3cne
云空5 小时前
《DeepSeek 网页/API 性能异常(DeepSeek Web/API Degraded Performance):网络安全日志》
运维·人工智能·web安全·网络安全·开源·网络攻击模型·安全威胁分析
深度Linux5 小时前
Linux网络编程中的零拷贝:提升性能的秘密武器
linux·linux内核·零拷贝技术
没有名字的小羊5 小时前
Cyber Security 101-Build Your Cyber Security Career-Security Principles(安全原则)
运维·网络·安全
m0_465215796 小时前
TCP & UDP Service Model
服务器·网络·tcp/ip
千夜啊6 小时前
Nginx 运维开发高频面试题详解
运维·nginx·运维开发
存储服务专家StorageExpert7 小时前
答疑解惑:如何监控EMC unity存储系统磁盘重构rebuild进度
运维·unity·存储维护·emc存储
chian-ocean8 小时前
从理论到实践:Linux 进程替换与 exec 系列函数
linux·运维·服务器