Linux 进程信号

文章目录

  • 信号的概览
  • 信号的产生
  • 信号的处理
  • 信号集操作
  • 信号的捕捉
  • 补充与说明

信号的概览

信号由软件或硬件产生发送给进程,进程对其做相应处理。信号是进程之间事件异步通知的一种方式,属于软中断

Linux下的全部信号由指令kill -l查询
在这里插入图片描述
Linux 下指令的说明使用指令 man 7 signal 查询
在这里插入图片描述

信号的产生

  1. 中断组合按键产生信号:如果前台有进程正在运行用可通过Ctrl + CCtrl + \ 的按键组合来向再在运行的前台进程发送信号,达到终止这个前台进程的目的。Ctrl+C:对应的信号是SIGINT(中断信号),Ctrl+\:对应的信号是SIGQUIT(退出信号),两者的区别是 SIGQUIT 信号会产生用于调试的核心转储文件(Core Dump),用于调试错误。
    在这里插入图片描述

  2. 进程间通信:在中端使用kill指令,可发送信号给指定进程,kill指令的使用方法为:
    kill -s <信号编号或名称> <进程ID>。 另外父子进程的状态通知等情境也会有进程间相互发送信号的情况,在补充说明部分会提到。

  3. 软件条件:在编程中,可以使用特定的函数(如raise())来主动产生信号。说的更简单点,在进程中的代码中,用户可通过使用int kill(pid_t pid, int signo); int raise(int signo); void abort(void);等函数生成信号,这些函数的具体使用在补充说明部分。

  4. 硬件异常:硬件异常,如除零错误、非法内存访问等,会导致操作系统产生相应的信号。
    硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

信号的处理

信号处理的方式:

  1. 执行信号默认的方案
  2. 忽略该信号,即接收到了一个信号但不采取任何行动。需要注意的是有些信号无法被忽略,因为如果可以全部忽略,那将可能会产生一个无法杀死进程。
  3. 捕获,捕获指定是更改信号对应的默认方案。

信号的状态:

  1. 递达: 实际执行信号的处理动作称为信号递达(Delivery) ,可以将递达理解为处理信号中
  2. 未决 : 信号从产生到递达之间的状态,称为信号未决(Pending)。
  3. 阻塞: 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作. 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

信号产生以后交付的对象是进程,最终执行信号对应方案的是OS(因为OS管理进程)。信号产生不是立即处理的,处理的方式也是要记录的。所以要有一个记录已产生的信号以及记录信号处理方式的地方,如下图所示,这个地方就在tast_struct中:在这里插入图片描述
上图中的block、pendin结构,被称为信号集( sigset_t)(类似于位图),图中block就是block信号集,pending就是pending信号集 。所处位置的下标代表是几号信号。 block代表信号是否被设置为阻塞,pending带表信号是否处于未决状态。handler是存储的是指向的是处理对应信号的函数方法,这个方法可以用户自定义。

注意: 不会出现同一个信号block位为0,pending为1的情况。因为peding代表有信号在等待处理,等待的原因是因为被设置为阻塞(block被置为1)。 之所以这样设计,是因为block为在后续有可能因为某些原因被重新设置为0,即取消阻塞。这时候信号就可以到递达状态了。

信号集操作

#include <signal.h>
int sigemptyset(sigset_t *set); // 初始化信号集中所有标志位为0
int sigfillset(sigset_t *set);  // 初始化信号集中所有标志位为1
int sigaddset (sigset_t *set, int signo);  //向信号集中添加有效信号
int sigdelset(sigset_t *set, int signo);   //从信号集中删除有效信号
int sigismember(const sigset_t *set, int signo);  
//判断一个信号集中signo信号标志位是否为1 。
  1. 使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号

sigprocmask

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

功能:设置阻塞信号集
参数:

  1. how 参数
    在这里插入图片描述
  2. set 输入参数,要设置的阻塞信号集。
  3. oset 输出参数 接收函数设置前的阻塞信号集。

sigpending

#include <signal.h>
int sigpending(sigset_t *set);

功能:获取未决定信号集。
参数:

  1. set 输出参数,接收未决信号集。

上述函数使用范例:
范例一: 函数的使用

#include <stdio.h>
#include <signal.h>

int main() {
    sigset_t set;
    sigemptyset(&set); // 清空信号集

    // 添加要阻塞的信号
    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGQUIT);

    // 设置信号屏蔽字
    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
        perror("sigprocmask");
        return 1;
    }

    printf("信号屏蔽字设置成功,按下Ctrl+C或Ctrl+\\将不会终止程序。\n");

    // 模拟程序运行
    int i;
    for (i = 0; i < 10; i++) {
        printf("程序正在运行,i = %d\n", i);
        sleep(1);
    }

    return 0;
}

范例二:验证信号的未决状态

#include "stdlib.h"
#include "unistd.h"
#include "signal.h"
#include "stdio.h"

void handler(sigset_t *set)
{

    while (true)
    {
        sleep(1) ;

        sigpending(set);
        int i = 0 ;
        for(; i < 32 ; i++)
        {
            if(sigismember(set, i))
            {
                printf("1") ;
            }
            else
            {
                printf("0");
            }
        } 
        printf("\n") ; 
    }
}


int main()
{
    sigset_t test_1 , test_2 ; 
    sigemptyset(&test_1);
    sigaddset(&test_1, SIGINT); 
    sigprocmask(SIG_BLOCK, &test_1, nullptr);

    handler(&test_2) ; 

    return 0 ; 
}

在这里插入图片描述

信号的捕捉

在这里插入图片描述

  1. 内核和用户态:
    根据对进程地址空间的学习,我们知道操作系统内核的代码在内存地址空间的前段存储,实际上由于虚拟内存机制,所有进程的都使用同一块物理内存上的内核代码,进程地址空间的内核代码地址通过专门的内核级页表找到物理内存上的内核代码。 当用户进行系统调用或者程序出现异常时,程序执行进入内核态,这里所说的进入内核态指的是执行流在出现以上情况时在进程地址空间中从用户空间进入到内核空间的中执行代码。执行流在内核态执行完需要执行的代码后需要返回用户态,即执行流从内核空间到用户空间,这一过程会进行信号的检测和处理。 实际上cpu通常具右两套寄存器,分别负责用户空间代码和内核空间代码的运行,在补充说明部分有更详细的解释。

  2. 信号从产生到捕捉的流程图:
    解释:
    ①如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了
    ② 对图中修改sigset_t的解释:
    当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字(将block信号集对应的信号置为1),当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。 这样可以防止如果捕捉信号后处理时,自定义的信号处理函数还未执行完,再次捕捉到信号时发生时可以记录这些来不及处理的信号。

在这里插入图片描述
信号捕捉到处理完毕,内核态和用户态的切换情况:
在这里插入图片描述

补充与说明

子进程父进程信号通信

子进程终止:当子进程终止时,它会向父进程发送一个SIGCHLD信号,父进程可以通过捕获该信号来处理子进程的终止状态。事实上,由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调 用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不 会产生僵尸进程,也不会通知父进程。系统默认的忽略动作和用户用sigaction函数自定义的忽略 通常是没有区别的,但这是一个特例。此方法对于Linux可用,但不保证在其它UNIX系统上都可 用。

中断概念

中断的相关概念 (了解即可) 中断的分类: 硬件中断(外部中断,异步中断):可屏蔽中断和不可屏蔽中断。
软件中断(内部中断,同步中断):异常和系统调用。 中断又分为硬中断和软中断。
硬中断:就是由系统硬件产生的中断。系统硬件通常引起外部事件。外部事件具有随机性和突发性,因此硬中断也具有随机性和突发性。
软中断:是执行中断指令时产生的。软中断不用外设施加中断请求信号,因此中断的发生不是随机而是由程序安排好的。 中断又分为外部中断和内部中断。
外部中断一般是指计算机外设发出的中断请求。外部中断是可以通过编程方式屏蔽的。例如键盘中断,打印机中断,定时器中断等。
内部中断是指因硬件出错(突然掉电,奇偶校验错误)或者运算错误(除数为零,运算溢出,单步中断)所引起的中断。内部中断是不可屏蔽的中断。大多数内部中断都是由Linux内核进行处理的,所以驱动程序员往往不需要关系这些问题。
中断又分为同步中断和异步中断。 同步中断是指令执行的过程中有CPU控制的,CPU在执行完一条指令后才发出中断。
异步中断是由硬件设备随机产生的,产生中断时并不考虑与处理器的时钟同步问题,该类型的中断时可以随时产生的。

中断信号线(IRQ):
中断信号线是对中断输入线和中断输出线的统称。中断输入线是指接收中断信号的引脚。中断输出线是指发送中断信号的引脚。每一个能够产生中断的外设都有一条或者多条中断输出线,用来通知处理器产生中断。相应地,处理器也有一组中断输入线,用来接收连接到它的外部设备发出的中断信号。
中断控制器:
中断控制器位于ARM处理器核心和中断源之间。外部中断源将中断发到中断控制器。中断控制器根据优先级进行判断,然后通过引脚将中断请求发送给ARM处理器核心。

CPU寄存器
CPU(中央处理器)通常具有两套寄存器,分别是用户可见寄存器和控制寄存器。

用户可见寄存器:

功能:用户可见寄存器用于存储程序执行过程中的数据和运算结果。
使用:程序员可以直接访问和使用这些寄存器,将数据存储在这些寄存器中,进行运算和操作。
示例:通用寄存器(如EAX、EBX、ECX、EDX等)用于存储临时数据和运算结果;索引寄存器、指针寄存器、标志寄存器等用于支持内存寻址和程序控制。

控制寄存器:

功能:控制寄存器用于控制CPU的工作状态和行为。 使用:控制寄存器通常由操作系统或特权级别较高的程序使用,用于控制CPU的工作模式和特性。
示例:程序计数器(PC)用于存储下一条要执行的指令的地址;状态寄存器(PSW或FLAGS)用于存储程 序运行过程中的状态信息,如条件码、进位标志等;控制寄存器用于控制特权级、中断使能等。

函数使用

signal函数 signal函数用于设置信号处理函数,用于捕获和处理特定信号的发生。它的原型如下:
#include <signal.h>

void (*signal(int signum, void (*handler)(int)))(int);

其中,signum表示要设置处理函数的信号编号,handler表示要设置的信号处理函数。 signal函数有以下几种用法:
1.注册信号处理函数: void handler(int signum) {
// 处理信号的代码 }

signal(SIGINT, handler);

上述代码将SIGINT信号(即键盘中断信号)的处理函数设置为handler函数。当接收到SIGINT信号时,将调用handler函数进行处理。
2.恢复默认信号处理行为: signal(SIGINT, SIG_DFL);

上述代码将SIGINT信号的处理方式恢复为默认行为。即当接收到SIGINT信号时,将执行默认的中断操作。
3.忽略信号:

signal(SIGINT, SIG_IGN);

上述代码将SIGINT信号的处理方式设置为忽略。即当接收到SIGINT信号时,不做任何处理

kill函数 在C/C++编程中,kill函数用于向指定的进程发送信号。它的原型如下:
#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

其中,pid表示要发送信号的进程ID,sig表示要发送的信号编号。

raise函数 在C/C++编程中,raise函数用于向当前进程自身发送信号。它的原型如下: #include
<signal.h>

int raise(int sig);

其中,sig表示要发送的信号编号。

abort函数

abort函数用于异常终止程序的执行。它的原型如下:
#include <stdlib.h>

void abort(void);

abort函数会导致程序立即终止,并生成一个终止信号(SIGABRT)来通知操作系统。操作系统会接收到该信号后,会进行相应的处理,例如生成core文件以供调试分析。

abort函数通常用于以下情况:
1.致命错误:当程序遇到无法恢复的错误或异常情况时,可以使用 abort函数终止程序的执行。这可以帮助开发人员在发生错误时及时发现问题并进行调试。
2.断言失败:在调试过程中,可以使用断言( assert)来检查程序中的条件是否满足。当断言失败时,abort函数可以用于终止程序的执行,并在控制台上显示相关的错误信息。
需要注意的是,abort函数会直接终止程序的执行,不会执行任何清理操作,例如关闭文件、释放内存等。因此,在调用abort函数之前,应该确保已经处理了必要的清理工作。
另外,abort函数不会捕获信号,而是生成一个终止信号。如果需要在程序中捕获和处理信号,可以使用signal函数或sigaction函数来注册信号处理函数。

alarm函数 alarm函数用于设置定时器,当定时器到达指定的时间后,会触发一个SIGALRM信号。它的原型如下:
c复制代码#include <unistd.h>

unsigned int alarm(unsigned int seconds);

其中,seconds表示要设置的定时器时间,单位为秒。alarm函数返回之前设置的定时器剩余时间。

需要注意的是,alarm函数只能设置一个全局的定时器,如果之前已经设置了定时器,调用alarm函数会取消之前的定时器并设置新的定时器。如果将seconds参数设置为0,则会取消当前的定时器。
另外,alarm函数在多线程程序中使用时需要格外小心,因为它是一个全局的定时器,可能会对其他线程产生干扰。在多线程程序中,可以考虑使用线程专用的定时器函数,如timer_create和timer_settime。

alarm 构成循环

alarm 构成循环 :

> #include <stdio.h>
> #include <unistd.h>
> #include <signal.h>
> 
> void handler(int signum) {
>     printf("Received SIGALRM signal\n");
>     alarm(3); }
> 
> int main() {
>     signal(SIGALRM, handler);
>     unsigned int remaining = alarm(3);
> 
>    while(1)
>      cout << 1 ; 
> 
>    return 0;
>    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/257445.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL进阶理论篇(十):数据库中的锁

文章目录 简介按照锁的粒度进行划分从数据库管理的角度进行划分从程序员的角度进行划分为什么共享锁会发生死锁&#xff1f;参考文献 简介 索引和锁&#xff0c;是数据库中的两个核心知识点。 索引的相关知识点&#xff0c;在之前的几章里我们已经介绍的差不多了。接下来我们…

结构体基础例题

这里写目录标题 例题一例题解析答案 例题二例题解析答案 例题三例题解析答案 例题四例题解析答案 例题五例题解析及答案 例题六例题解析及答案 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978…

如何在大型Python项目中优雅地使用Dotenv

如何在大型Python项目中优雅地使用Dotenv 引言Dotenv的基本概念什么是Dotenv&#xff1f;Dotenv的工作原理如何在项目中使用Dotenv 环境变量与大型项目配置环境变量的重要性管理不同环境的配置使用Dotenv优化配置管理 Dotenv的高级应用动态配置与环境切换安全性和敏感数据管理与…

深入了解Linux信号:作用、产生、捕捉和阻塞

这里写目录标题 引言1. 信号的基本概念1.1 信号的分类和编号&#xff1a;1.2 查看信号默认处理动作1.3 信号的作用1.4 信号的产生1.4.1通过终端按键产生1.4.2通过系统函数向进程发信号1.4.3由软件条件产生信号1.4.4硬件异常产生信号 2. 常见信号及其作用SIGINT (2) - 中断信号&…

智慧水务系统解决了哪些问题

随着城市化进程的加快&#xff0c;水资源的日益紧缺和水环境问题的日益突出&#xff0c;传统的水务管理方式已经无法满足现代城市的需求。为了解决这些问题&#xff0c;智慧水务系统应运而生。 一、智慧水务系统概述 智慧水务系统是一种基于物联网、大数据、云计算等先进技术…

回归预测 | MATLAB实现NGO-SCN北方苍鹰算法优化随机配置网络的数据回归预测 (多指标,多图)

回归预测 | MATLAB实现NGO-SCN北方苍鹰算法优化随机配置网络的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现NGO-SCN北方苍鹰算法优化随机配置网络的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍…

前端检测字符串中是否含有特殊字符,并返回该特殊字符

一、判断字符串中是否含有特殊字符 const hasSpecicalCharacter (str) > {var regex /[!#$%^&*(),.?":{}|<>]/return regex.test(str) } //含有特殊字符返回true, 没有特殊字符返回false 二、判断字符串中是否含有特殊字符&#xff0c;并返回该特殊字符…

作业--day32

机械臂 #include <myhead.h>#define PORT 8888 #define IP "192.168.125.59"int main(int argc, const char *argv[]) {int sfd socket(AF_INET, SOCK_STREAM, 0);if(sfd -1){perror("socket error");return -1;}int reuse -1;if(setsockopt(sfd…

C# 调用腾讯混元大模型

写在前面 今天用C#调用了一下腾讯混元大模型&#xff0c;调用代码贴一下&#xff0c;具体的效果等深入使用后再来评价。 GitHub - TencentCloud/tencentcloud-sdk-dotnet: Tencent Cloud API 3.0 SDK for .NET 腾讯混元大模型简介_腾讯混元大模型购买指南_腾讯混元大模型操作…

等保测评主要保护哪些方面的安全?

等保测评是经公安部认证的具有资质的测评机构&#xff0c;依据国家信息安全等级保护规范规定&#xff0c;受有关单位委托&#xff0c;按照有关管理规范和技术标准&#xff0c;对信息系统安全等级保护状况进行检测评估的活动。那么企业做等保“保”的是什么呢&#xff1f; 等保主…

直播江湖:东方甄选与董宇辉的权力游戏

出品| 大力财经 文 | 魏力 近期&#xff0c;围绕东方甄选的小作文事件引起了广泛关注&#xff0c;有人将其解读为一场巧妙策划的事件营销&#xff0c;然而&#xff0c;舆情的不可控性使得事态逐渐演变为一场复杂的利益博弈。 东方甄选与董宇辉的“蜜月期”可以说是双方互相成就…

直流电、交流电和发电机、接地、变压器

直流电 此节内容主要摘录自&#xff1a;图文详解直流电与直流电路基本知识 直流电是指电流方向不随时间作周期性变化&#xff0c;由正极流向负极&#xff0c;但电流的大小可能会变化的电流。直流电可以分为稳定&#xff08;恒定&#xff09;直流和脉动直流两种&#xff0c;如下…

Re解析(正则表达式解析)

正则表达式基础 元字符 B站教学视频&#xff1a; 正则表达式元字符基本使用 量词 贪婪匹配和惰性匹配 惰性匹配如下两张图&#xff0c;而 .* 就表示贪婪匹配&#xff0c;即尽可能多的匹配到符合的字符串&#xff0c;如果使用贪婪匹配&#xff0c;那么结果就是图中的情况三 p…

ZKP Commitment (1)

MIT IAP 2023 Modern Zero Knowledge Cryptography课程笔记 Lecture 5: Commitment 1 (Ying Tong Lai) Overview: Modern SNARK IOP: Interactive Oracle ProofCommitment SchemeIOP “compiled by” the commitment scheme to get a non-interactive proofAn IOP is “inform…

【重点】【前缀树|字典树】208.实现Trie(前缀树)

题目 前缀树介绍&#xff1a;https://blog.csdn.net/DeveloperFire/article/details/128861092 什么是前缀树 在计算机科学中&#xff0c;trie&#xff0c;又称前缀树或字典树&#xff0c;是一种有序树&#xff0c;用于保存关联数组&#xff0c;其中的键通常是字符串。与二叉查…

windows10-tdengine的安装及使用

win10-tdengine的安装及使用 一、下载及安装配置1.1 下载安装1.2 配置 二、启动及关闭服务2.1 启动tdengine服务2.2 关闭tdengine服务2.2 开机自启动配置 四、可视化工具&#xff08;GUI&#xff09;4.1 下载4.2 安装 五、TDengine 命令行&#xff08;CLI&#xff09;5.1 进入命…

st.pp.normalize_total(data) # NOTE: no log1p

这段代码在使用 stlearn 包中的 st.pp.normalize_total 函数对数据进行总体计数标准化。标准化后&#xff0c;每个细胞的总计数都将等于 median(total_counts)。 NOTE: no log1p 这行注释表示在标准化后&#xff0c;数据不会进行 log1p 转换。log1p 转换将每个计数值增加 1&a…

【每日一题】1901. 寻找峰值 II-2023.12.19

题目&#xff1a; 1901. 寻找峰值 II 一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子(上、下、左、右)的元素。 给你一个 从 0 开始编号 的 m x n 矩阵 mat &#xff0c;其中任意两个相邻格子的值都 不相同 。找出 任意一个 峰值 mat[i][j] 并 返回其位置 [i,j] 。 你…

Java对象结构

Java 对象(Object 实例)结构包括三部分:对象头、对象体、对齐字节。 Object的三个部分 对象头包括三个字段&#xff0c;第一个字段叫做 Mark Word(标记字)&#xff0c;用于存储自身运行时的数据 例如 GC 标志位、哈希码、锁状态等信息。 第二个字段叫做 Class Pointer(类对象…

CEC2013(python):五种算法(HHO、WOA、GWO、DBO、PSO)求解CEC2013(python代码)

一、五种算法简介 1、哈里斯鹰优化算法HHO 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、蜣螂优化算法DBO 5、粒子群优化算法PSO 二、5种算法求解CEC2013 &#xff08;1&#xff09;CEC2013简介 参考文献&#xff1a; [1] Liang J J , Qu B Y , Suganthan P N , et al. P…