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;
>    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

H264码流打包分析

H264码流打包分析 SODB 数据比特串&#xff0d;&#xff0d;&#xff1e;最原始的编码数据 RBSP 原始字节序列载荷&#xff0d;&#xff0d;&#xff1e;在SODB的后面填加了结尾比特&#xff08;RBSP trailing bits 一个bit“1”&#xff09;若干比特“0”,以便字节对齐。…

Ubuntu 虚拟机环境,编译AOSP源码

环境 : VMware虚拟机 Ubuntu 20.04.3 LTS 搭建配置开发环境 sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl…

创投课程第五期 | 超越比特币:探索BTC生态的无限可能

协会邀请了来自水滴资本&#xff08;Waterdrip Capital&#xff09;的投资总监——Elaine&#xff0c;作为VC创投课程第5期的嘉宾&#xff0c;在北京时间12月17日(周日)晚上21:00 PM-22:00 PM&#xff0c;届时将与所有对Web3投资、创业心怀热忱的朋友们共同探讨《超越比特币&am…

C语言 文件I/O(备查)

所有案列 跳转到其他。 文件打开 FILE* fopen(const char *filename, const char *mode); 参数&#xff1a;filename&#xff1a;指定要打开的文件名&#xff0c;需要加上路径&#xff08;相对、绝对路径&#xff09;mode&#xff1a;指定文件的打开模式 返回值&#xff1a;成…

模型评估:压力测试 模拟对手 对齐 智能对抗 CAPTCHA(全自动区分计算机和人类的公共图灵测试)

对齐&#xff0c;智能对抗&#xff1a;魔高一尺&#xff0c;道高一丈。用更高的智能去对抗恶意使用。openAI一半的内容都在讲这个&#xff0c;但没有讲具体的方法。 如果认为对方是一个人就通过了图灵测试&#xff0c;真正的实现了智能。 如果智能达到了这种程度&#xff0c;智…

Tapdata 亮相 2023 谷歌出海创业加速器展示日活动,实时数据点亮企业创新之路

12 月 6 日&#xff0c;经过 2023 谷歌出海创业加速器对入营企业为期 3 个月的辅导及培训&#xff0c; 其毕业典礼及展示日活动在北京举行。 经历三个月的沉淀&#xff0c;来自不同行业&#xff0c;包含 Tapdata 在内的 10 家入营企业集中亮相&#xff0c;相互分享各自产品的创…

XUbuntu22.04之npm解决pm WARN deprecated(一百九十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

PostgreSQL常用命令

数据库版本 :9.6.6 注意 :PostgreSQL中的不同类型的权限有 SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER,CREATE,CONNECT,TEMPORARY,EXECUTE 和 USAGE。 1. 登录PG数据库 以管理员身份 postgres 登陆,然后通过 #psql -U postgres #sudo -i -u postgres …

数据手套:手势识别技术的多元化应用

科技日新月异&#xff0c;虚拟现实不再局限于依赖头显来探索虚拟世界。数据手套的广泛应用使人们能够更轻松地与虚拟世界产生真实互动。在此之中&#xff0c;手势识别作为一种直观、自然的人机交互方式&#xff0c;受到了广泛关注。数据手套作为一种能够精确捕捉手指运动的人机…

Flink系列之:监控Checkpoint

Flink系列之&#xff1a;监控Checkpoint 一、概览二、概览&#xff08;Overview&#xff09;选项卡三、历史记录&#xff08;History&#xff09;选项卡四、历史记录数量配置五、摘要信息&#xff08;Summary&#xff09;选项卡六、配置信息&#xff08;Configuration&#xff…

@RequestMapping注解与其派生注解接收参数详解

一、前言 根据 HTTP 标准&#xff0c;HTTP 请求可以使用多种请求方法。 HTTP1.0 定义了三种请求方法&#xff1a; GET, POST 和 HEAD 方法。 HTTP1.1 新增了六种请求方法&#xff1a;OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。 RequestMapping注解与其派生注解 在…

Qt实现动画的2种方式

由于我之前是写java的所以在学习Qt的时候感觉会有点熟悉&#xff0c;因为Qt就是 用c写&#xff0c;而java底层也是c实现的 先看效果&#xff1a; 一、使用QMovie 这种方式我目前是用来加载gif图的&#xff0c;很简单噢&#xff0c;只不过我是加载的本地的路径&#xff0c;如…

数据安全无阻,轻松远程工作!迅软DSE出差加密指南,让你出差更放心!

文件加密软件是确保内网文件安全使用的重要工具&#xff0c;但在终端脱离内部网络、面对外出或居家办公等情境时&#xff0c;文件加密的挑战也相应增加。为解决这一问题&#xff0c;迅软DSE文件加密软件提供了离线授权功能&#xff0c;确保在终端脱离公司网络后的设定时间内&am…

抖店怎么运营?新手运营方法,这几个流程告诉你!

我是电商珠珠 抖店的热度一直很高&#xff0c;很多新手在入驻抖店的时候&#xff0c;并不知道怎么去运营。 其实&#xff0c;从开店到店铺维护&#xff0c;这几个步骤你们一次就能看懂。 第一步&#xff0c;入驻 入驻的时候需要准备一张个体的营业执照、身份证、银行卡、手…

泽众一站式自动化测试平台TestOne,自动化测试的整体框架和功能介绍

TestOne是泽众软件自主研发的一体化测试系统&#xff0c;基于 B/S 体系的微服务架构&#xff0c;集系统管理、项目管理、测试管理、缺陷管理等功能于一体&#xff0c;覆盖了GUI界面功能自动化测试、接口自动化测试、移动自动化测试类型&#xff0c;完整覆盖自动化测试项目的全过…

LoadRunnder介绍

LoadRunner介绍安装教程LoadRunner三大组件 LoadRunner介绍 性能测试的定义&#xff1a;测试人员借助性能测试工具&#xff0c;模拟系统在不同场景下&#xff0c;对应的性能指标是否达到预期 定义中这个工具是什么呢&#xff1f; 可以使用LoadRunner测试 这个工具相比于其它工…

智能优化算法应用:基于入侵杂草算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于入侵杂草算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于入侵杂草算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.入侵杂草算法4.实验参数设定5.算法结果6.…

【LeetCode刷题笔记(8-3)】【Python】【接雨水】【双指针】【困难】

文章目录 引言接雨水题目描述提示 解决方案3&#xff1a;【双指针】结束语 接雨水 【LeetCode刷题笔记&#xff08;8-1&#xff09;】【Python】【接雨水】【动态规划】【困难】 【LeetCode刷题笔记&#xff08;8-2&#xff09;】【Python】【接雨水】【单调栈】【困难】 引言…

Arcgis新建矢量并手动绘制范围

新建一个shapefile&#xff0c;并选择面 得到了一个新shape 然后右击&#xff0c;开始编辑&#xff0c;打开编辑器

issue queue的实现方式

主要从一下几个点进行考虑&#xff1a; 集中式&#xff08;Centrallized&#xff09;或者分布式(Distributed)&#xff1b;压缩式&#xff08;Compressing&#xff09;或者非压缩式(Non-compressing)&#xff1b;数据捕捉的方式&#xff08;Data-capture&#xff09;或者非数据…