进程间通信——信号

信号的概念

  • 信号是 Linux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
  • 发往进程的诸多信号,通常都是源于内核。引发内核为进程产生信号的各类事件如下:

----◼ 对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号。比如输入Ctrl+C 通常会给进程发送一个中断信号。

----◼ 硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给相关进程。比如执行一条异常的机器语言指令,诸如被 0 除,或者引用了无法访问的内存区域。

----◼ 系统状态变化,比如 alarm 定时器到期将引起 SIGALRM 信号,进程执行的 CPU 时间超限,或者该进程的某个子进程退出。

----◼ 运行 kill 命令或调用 kill 函数。

使用信号的两个主要目的是:

----◼ 让进程知道已经发生了一个特定的事情。

----◼ 强迫进程执行它自己代码中的信号处理程序。

信号的特点:

----◼ 简单

----◼ 不能携带大量信息

----◼ 满足某个特定条件才发送

----◼ 优先级比较高

查看系统定义的信号列表:kill –l

前 31 个信号为常规信号,其余为实时信号。

Linux 典型信号

编号信号名称对应事件默认动作
2SIGINT当用户按下了组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号终止进程
3SIGQUIT用户按下组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号终止进程
9SIGKILL无条件终止进程。该信号不能被忽略,处理和阻塞终止进程,可以杀死任何进程
11SIGSEGV指示进程进行了无效内存访问(段错误)终止进程并产生core文件
13SIGPIPEBroken pipe 向一个没有读端的管道写数据终止进程
17SIGCHLD子进程结束时,父进程会收到这个信号忽略这个信号
18SIGCONT如果进程已停止,则使其继续运行继续/忽略
19SIGSTOP停止进程的执行。信号不能被忽略,处理和阻塞为终止进程

信号的 5 种默认处理动作

查看信号的详细信息:man 7 signal
信号的 5 种默认处理动作
----◼ Term 终止进程

----◼ Ign 当前进程忽略掉这个信号

----◼ Core 终止进程,并生成一个 Core 文件用来保存进程异常退出的错误信息

----◼ Stop 暂停当前进程

----◼ Cont 继续执行当前被暂停的进程

信号的几种状态:产生、未决、递达

SIGKILL 和 SIGSTOP 信号不能被捕捉、阻塞或者忽略,只能执行默认动作。

信号相关的函数

kill 函数

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

作用:给任何的进程或者进程组发送任何的信号(sig)

参数:
---- pid:
-------- > 0:将信号发送给指定的进程
-------- = 0:将信号发送给当前的进程组
-------- = -1:将信号发送给每一个有权限接受这个信号的进程
-------- < -1:这个 pid 就是某个进程组的 ID 的相反数,给这个进程组发送信号
------- sig:需要发送的信号的编号或者是宏值,0 表示不发送任何信号

例子:

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>int main()
{pid_t pid = fork();if (pid == 0) {//子进程for (int i = 0; i < 5; ++i) {printf("child \n");sleep(1);}}else if (pid > 0) {//父进程printf("parent\n");sleep(2);printf("kill child\n");kill(pid, 9);}return 0;
}

raise 函数

#include <signal.h>
int raise(int sig);

作用:给当前进程发送信号
参数:要发送的信号
返回:成功返回 0,失败返回非 0
等价于:kill(getpid(), sig);

abort 函数

#include <stdlib.h>
void abort(void);

作用:发送 SIGABORT 信号给当前进程,杀死当前进程
等价于:kill(getpid(), SIGABORT);

alarm 函数

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

作用:设置定时器(闹钟),函数调用开始倒计时,当倒计时为 0 的时候,函数会给当前的进程发送一个信号:SIGALRM 默认终止当前的进程,每一个进程都有且只有唯一的一个定时器。

参数:倒计时的时长,单位是秒,参数为 0 表示定时器无效(不进行倒计时,不发信号)。可以通过传递 0 参数来取消一个定时器。

返回:

---- 之前没有定时器:返回 0

---- 之前有定时器:返回之前的定时器倒计时剩余时间

alarm 与进程的状态(运行、阻塞等)无关。

首先设置一个 5 秒的定时器,之后修改为 10 秒,过了 10 秒时候发送 SIGALRM 信号默认关闭进程:

#include <unistd.h>
#include <stdio.h>int main()
{int seconds = alarm(5);printf("seconds = %d\n", seconds);sleep(2);seconds = alarm(10);printf("seconds = %d\n", seconds);while (1) {}return 0;
}

在这里插入图片描述
setitimer 函数

#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_val, struct itimerval *old_value);

作用:设置定时器,可以替代 alarm 函数。精度是微秒(us)。

参数:

---- which:定时器以什么时间定时

-------- ITIMER_REAL:真实时间,时间到达后发送 SIGALRM ,是最常用的

-------- ITIMER_VIRTUAL:用户时间,时间到达后发送 SIGVTALRM

-------- ITIMER_PROF:以该进程在用户态和内核态下所消耗的时间来计算,时间到达后发送 SIGPROF

---- new_val:设置定时器的属性

struct itimerval {  //定时器结构体struct timeval it_interval;  //每个阶段的时间,间隔时间struct timeval it_value;  //延迟多长时间执行定时器
};
struct timeval {  //时间的结构体time_t      tv_sec;  //秒数suseconds_t tv_usec;  //微秒
};

---- old_value:记录上一次的定时的时间参数,一般不使用,指定 NULL

返回:成功返回 0,失败返回 -1

信号捕捉函数 signal 函数

#include <signal.h>
sighandler_t signal(int signum, sighandler_t handler); 

作用:设置某个信号的捕捉行为

参数:

---- signum:要捕捉的信号

---- handler:捕捉到信号要如何处理

-------- SIG_IGN:忽略信号

-------- SIG_DFL:使用信号默认的行为

-------- 回调函数:函数指针类型,由内核调用,程序员只负责提前写好捕捉到信号后如何处理信号

返回:

---- 成功:返回上一次注册的信号处理函数的地址。第一次调用返回 NULL

---- 失败:返回 SIG_ERR,设置错误号

过3秒之后定时开始,每隔2秒钟定时一次的示例:

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>void myalarm(int num)
{printf("捕捉到了的信号的编号是:%d\n", num);printf("xxxxxx\n");
}int main()
{//注册信号捕捉__sighandler_t ret0 = signal(SIGALRM, myalarm);if (ret0 == SIG_ERR) {perror("signal");exit(0);}struct itimerval new_value;//设置间隔时间new_value.it_interval.tv_sec = 2;new_value.it_interval.tv_usec = 0;//设置延迟时间new_value.it_value.tv_sec = 3;new_value.it_value.tv_usec = 0;int ret = setitimer(ITIMER_REAL, &new_value, NULL);printf("定时器开始了\n");if (ret == -1) {perror("setitimer");exit(0);}getchar();return 0;
}

程序三秒之后输出“定时器开始了”,然后每隔两秒输出后面的内容
在这里插入图片描述

信号集

  • 许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t。
  • 在 PCB 中有两个非常重要的信号集。一个称之为 “阻塞信号集” ,另一个称之为“未决信号集” 。这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对PCB 中的这两个信号集进行修改。
  • 信号的 “未决” 是一种状态,指的是从信号的产生到信号被处理前的这一段时间。 信号的 “阻塞”是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。

阻塞信号集和未决信号集:

  1. 用户通过键盘 Ctrl + C, 产生 2 号信号 SIGINT (信号被创建)

  2. 信号产生但是没有被处理 (未决)

     ---- 在内核中将所有的没有被处理的信号存储在一个集合中 (未决信号集)---- SIGINT 信号(2 号信号)状态被存储在第二个标志位上---- 如果这个标志位的值为 0, 说明信号不是未决状态---- 如果这个标志位的值为 1, 说明信号处于未决状态
    
  3. 这个未决状态的信号,需要被处理,处理之前需要和另一个信号集(阻塞信号集),进行比较

     ---- 阻塞信号集默认不阻塞任何的信号---- 如果想要阻塞某些信号需要用户调用系统的 API
    
  4. 在处理的时候和阻塞信号集中的标志位进行查询,看是不是对该信号设置阻塞了

     ---- 如果没有阻塞,这个信号就被处理---- 如果阻塞了,这个信号就继续处于未决状态,直到阻塞解除,这个信号就被处理
    

以下信号集相关的函数都是对自定义的信号集进行操作:

int sigemptyset(sigset_t *set);
  • 作用:清空信号集中的数据,将信号集中的所有的标志位置为 0
  • 参数:set,值-结果参数,需要操作的信号集
  • 返回:成功返回 0, 失败返回 -1
int sigfillset(sigset_t *set);
  • 作用:将信号集中的所有的标志位置为 1
  • 参数:set,值-结果参数,需要操作的信号集
  • 返回值:成功返回0, 失败返回 -1
int sigaddset(sigset_t *set, int signum);
  • 作用:设置信号集中的某一个信号对应的标志位为 1,表示阻塞这个信号 参数:
  • ---- set:值-结果参数,需要操作的信号集
  • ---- signum:需要设置阻塞的那个信号
  • 返回:成功返回 0, 失败返回 -1
int sigdelset(sigset_t *set, int signum);
  • 作用:设置信号集中的某一个信号对应的标志位为 0,表示不阻塞这个信号 参数:
  • ---- set:值-结果参数,需要操作的信号集
  • ---- signum:需要设置不阻塞的那个信号
  • 返回:成功返回 0, 失败返回 -1
int sigismember(const sigset_t *set, int signum);
  • 作用:判断某个信号是否阻塞

参数:

  • ---- set:需要操作的信号集
  • ---- signum:需要判断的那个信号

返回:

  • ---- 1 : signum被阻塞
  • ---- 0 : signum不阻塞
  • ---- -1 : 失败
    示例:
#include <signal.h>
#include <stdio.h>int main()
{//创建一个信号集sigset_t set;//清空信号集的内容sigemptyset(&set);//判断 SIGINT 是否在信号集 set 里int ret = sigismember(&set, SIGINT);if (ret == 0) {printf("SIGINT不阻塞\n");}else if (ret == 1) {printf("SIGINT阻塞\n");}//添加信号到信号集中sigaddset(&set, SIGINT);ret = sigismember(&set, SIGINT);if (ret == 0) {printf("SIGINT不阻塞\n");}else if (ret == 1) {printf("SIGINT阻塞\n");}//从信号集中删除一个信号sigdelset(&set, SIGINT);ret = sigismember(&set, SIGINT);if (ret == 0) {printf("SIGINT不阻塞\n");}else if (ret == 1) {printf("SIGINT阻塞\n");}return 0;
}

在这里插入图片描述
sigprocmask 函数

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • 作用:将自定义信号集中的数据设置到内核中(设置阻塞,解除阻塞,替换)

  • 参数:

    ---- how :如何对内核阻塞信号集进行处理

    -------- SIG_BLOCK::将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变,mask | set

    -------- SIG_UNBLOCK::根据用户设置的数据,对内核中的数据进行解除阻塞 mask &= ~set

    -------- SIG_SETMASK:覆盖内核中原来的值

    ---- set :已经初始化好的用户自定义的信号集

    ---- oldset : 保存设置之前的内核中的阻塞信号集的状态,可以是 NULL

  • 返回:成功返回 0,失败返回 -1 并设置错误号 EFAULT、EINVAL

SIGKILL 和 SIGSTOP 不能被捕捉,不能被忽略。

内核实现信号捕捉的过程

在这里插入图片描述

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

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

相关文章

手机商城网站的分析与设计(论文+源码)_kaic

目录 摘 要 1 1 绪论 2 1.1选题背景意义 2 1.2国内外研究现状 2 1.2.1国内研究现状 2 1.2.2国外研究现状 3 1.3研究内容 3 2 网上手机商城网站相关技术 4 2.1.NET框架 4 2.2Access数据库 4 2.3 JavaScript技术 4 3网上手机商城网站分析与设…

复古游戏库管理器RomM

什么是 RomM &#xff1f; RomM&#xff08;代表 Rom Manager&#xff09;是一个专注于复古游戏的游戏库管理器。通过 Web 浏览器管理和组织您的所有游戏。受 Jellyfin 的启发&#xff0c;允许您从现代界面管理所有游戏&#xff0c;同时使用 IGDB 元数据丰富它们。 RomM 支持的…

线上通过Nginx部署前端工程,并且配置SSL

介绍、为了更好的帮助大家学习&#xff0c;减少歧义,IP地址我就不隐藏了&#xff0c;公司也是我自己的公司。你们就别来攻击了。 下面给出步骤: 一、前期准备工作 通过在目标服务器上安装宝塔面板、安装redis、mysql、nginx、jdk环境等 1、 2、前端工程通过npm run build 打…

完美解决Github提交PR后报错:File is not gofumpt-ed (gofumpt)

问题阐述 最近在Github上提交PR后&#xff0c;遇到了这么一个问题&#xff1a;golangci-lint运行失败&#xff0c;具体原因是File is not gofumpt-ed (gofumpt)。 名词解释 golangci-lint&#xff1a; golangci-lint 是Go语言社区中常用的代码质量检查工具&#xff0c;它可以…

Redis的AOF持久化

除了RDB持久化功能之外&#xff0c;Redis还提供了AOF持久化功能。与RDB 持久化通过保存数据库中的键值对来记录数据库状态不同&#xff0c;AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的&#xff0c;如下图所示。 举个例子&#xff0c;如果我们对空白的数据…

【JVM】垃圾回收算法

目录 一、判断对象已“死” 1.1、引用计数算法 1.2、可达性分析算法 1.3、引用的概念 二、垃圾收集算法理论 2.1、分代收集理论 三、垃圾收集算法 3.1、标记--清除算法 3.2、标记--复制算法 3.3、标记--整理算法 一、判断对象已“死” 在堆里面存放着Java世界中几乎所…

大数据课程I4——Kafka的零拷贝技术

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 掌握Kafka的零拷贝技术&#xff1b; ⚪ 了解常规的文件传输过程&#xff1b; 一、常规的网络传输原理 表面上一个很简单的网络文件输出的过程&#xff0c;在OS底层&…

接口测试自动化:简化测试流程,提升效率

接口测试自动化&#xff1a;简化测试流程&#xff0c;提升效率 什么是接口测试自动化&#xff1f; 接口测试自动化是指使用特定的工具和技术来自动化执行接口测试的过程。通过编写脚本&#xff0c;自动化工具可以模拟用户与软件系统的交互&#xff0c;验证接口的功能和性能。…

函数递归专题(案例超详解一篇讲通透)

函数递归 前言1.递归案例:案例一&#xff1a;取球问题案例二&#xff1a;求斐波那契额数列案例三&#xff1a;函数实现n的k次方案例四&#xff1a;输入一个非负整数&#xff0c;返回组成它的数字之和案例五&#xff1a;元素逆置案例六&#xff1a;实现strlen案例七&#xff1a;…

使用巴特沃兹滤波器的1D零相位频率滤波研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Python源码05:使用Pyecharts画词云图图

**Pyecharts是一个用于生成 Echarts 图表的 Python 库。Echarts 是一个基于 JavaScript 的数据可视化库&#xff0c;提供了丰富的图表类型和交互功能。**通过 Pyecharts&#xff0c;你可以使用 Python 代码生成各种类型的 Echarts 图表&#xff0c;例如折线图、柱状图、饼图、散…

AI如何看待能力,学历,文凭

1 假设&#xff1a; {文凭}⊂{学历}⊂{能力} 2 证明&#xff1a; 首先&#xff0c;我们需要明确这些集合的定义和关系。 {能力}是一个包含各种能力的集合&#xff0c;例如学习能力、沟通能力、创新能力、领导能力和专业技能等。 {学历}是一个包含各种学历的集合&#xff0c…

没学C++,如何从C语言丝滑过度到python【python基础万字详解】

大家好&#xff0c;我是纪宁。 文章将从C语言出发&#xff0c;深入介绍python的基础知识&#xff0c;也包括很多python的新增知识点详解。 文章目录 1.python的输入输出&#xff0c;重新认识 hello world&#xff0c;重回那个激情燃烧的岁月1.1 输出函数print的规则1.2 输入函…

【Java从0到1学习】09 正则表达式

1. 正则表达式概述 在编写处理字符串的程序或网页时&#xff0c;经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说&#xff0c;正则表达式就是记录文本规则的代码。 正则表达式&#xff0c;又称正规表示法、常规表示法&#xff…

机器学习笔记:李宏毅diffusion model

1 概念原理 首先sample 一个都是噪声的vector然后经过denoise network 过滤一些杂质接着继续不断denoise&#xff0c;直到最后出来一张清晰图片 【类似于做雕塑&#xff0c;一开始只是一块石头&#xff08;噪声很杂的雕塑&#xff09;&#xff0c;慢慢雕刻出想要的花纹】 同一个…

飞天使-jenkins进行远程linux机器修改某个文件的思路

文章目录 jenkins配置的方式jenkins中执行shell的思路 jenkins配置的方式 jenkins中执行shell的思路 下面的脚本别照抄&#xff0c;只是一个思路 ipall"$ips"# 将文本参数按行输出为变量 while IFS read -r line; doecho "$line" if [[ ! -z $line ]] &…

Android CameraX适配Android13的踩坑之路

AndroidCameraX适配Android13的踩坑之路 前言&#xff1a; 最近把AGP插件升级到8.1.0&#xff0c;新建项目的时候目标版本和编译版本都是33&#xff0c;发现之前的demo使用Camerax拍照和录像都失败了&#xff0c;于是查看了一下官网和各种资料&#xff0c;找到了Android13的适…

PHP实践:分布式场景下的Session共享解决方案实现

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责…

LVS的负载均衡集群

基于四层协议进行 什么是集群 含义: 1、cluster 集群、群集 2、多台主机构成&#xff0c;但对外之表现为一个整体只提供一个访问入口&#xff08;域名和地址&#xff09;相当于一台大型计算机 目前互联网应用中&#xff0c;随着站点对硬件性能、响应速度、服务稳定性、数据…

使用 `tailwindcss-patch@2` 来提取你的类名吧

使用 tailwindcss-patch2 来提取你的类名吧 使用 tailwindcss-patch2 来提取你的类名吧 安装使用方式 命令行 Cli 开始提取吧 Nodejs API 的方式来使用 配置 初始化 What’s next? tailwindcss-patch 是一个 tailwindcss 生态的扩展项目。也是 tailwindcss-mangle 项目重要…