进程间通信——信号
信号的概念
- 信号是 Linux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
- 发往进程的诸多信号,通常都是源于内核。引发内核为进程产生信号的各类事件如下:
----◼ 对于前台进程,用户可以通过输入特殊的终端字符来给它发送信号。比如输入Ctrl+C 通常会给进程发送一个中断信号。
----◼ 硬件发生异常,即硬件检测到一个错误条件并通知内核,随即再由内核发送相应信号给相关进程。比如执行一条异常的机器语言指令,诸如被 0 除,或者引用了无法访问的内存区域。
----◼ 系统状态变化,比如 alarm 定时器到期将引起 SIGALRM 信号,进程执行的 CPU 时间超限,或者该进程的某个子进程退出。
----◼ 运行 kill 命令或调用 kill 函数。
使用信号的两个主要目的是:
----◼ 让进程知道已经发生了一个特定的事情。
----◼ 强迫进程执行它自己代码中的信号处理程序。
信号的特点:
----◼ 简单
----◼ 不能携带大量信息
----◼ 满足某个特定条件才发送
----◼ 优先级比较高
查看系统定义的信号列表:kill –l
前 31 个信号为常规信号,其余为实时信号。
Linux 典型信号
编号 | 信号名称 | 对应事件 | 默认动作 |
---|---|---|---|
2 | SIGINT | 当用户按下了组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号 | 终止进程 |
3 | SIGQUIT | 用户按下组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号 | 终止进程 |
9 | SIGKILL | 无条件终止进程。该信号不能被忽略,处理和阻塞 | 终止进程,可以杀死任何进程 |
11 | SIGSEGV | 指示进程进行了无效内存访问(段错误) | 终止进程并产生core文件 |
13 | SIGPIPE | Broken pipe 向一个没有读端的管道写数据 | 终止进程 |
17 | SIGCHLD | 子进程结束时,父进程会收到这个信号 | 忽略这个信号 |
18 | SIGCONT | 如果进程已停止,则使其继续运行 | 继续/忽略 |
19 | SIGSTOP | 停止进程的执行。信号不能被忽略,处理和阻塞 | 为终止进程 |
信号的 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 中的这两个信号集进行修改。
- 信号的 “未决” 是一种状态,指的是从信号的产生到信号被处理前的这一段时间。 信号的 “阻塞”是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。
阻塞信号集和未决信号集:
-
用户通过键盘 Ctrl + C, 产生 2 号信号 SIGINT (信号被创建)
-
信号产生但是没有被处理 (未决)
---- 在内核中将所有的没有被处理的信号存储在一个集合中 (未决信号集)---- SIGINT 信号(2 号信号)状态被存储在第二个标志位上---- 如果这个标志位的值为 0, 说明信号不是未决状态---- 如果这个标志位的值为 1, 说明信号处于未决状态
-
这个未决状态的信号,需要被处理,处理之前需要和另一个信号集(阻塞信号集),进行比较
---- 阻塞信号集默认不阻塞任何的信号---- 如果想要阻塞某些信号需要用户调用系统的 API
-
在处理的时候和阻塞信号集中的标志位进行查询,看是不是对该信号设置阻塞了
---- 如果没有阻塞,这个信号就被处理---- 如果阻塞了,这个信号就继续处于未决状态,直到阻塞解除,这个信号就被处理
以下信号集相关的函数都是对自定义的信号集进行操作:
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 不能被捕捉,不能被忽略。
内核实现信号捕捉的过程
相关文章:

进程间通信——信号
信号的概念 信号是 Linux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异…...
PAT 1039 Course List for Student
个人学习记录,代码难免不尽人意。 Zhejiang University has 40000 students and provides 2500 courses. Now given the student name lists of all the courses, you are supposed to output the registered course list for each student who comes for a query. …...
【Sklearn】基于决策树算法的数据分类预测(Excel可直接替换数据)
【Sklearn】基于决策树算法的数据分类预测(Excel可直接替换数据) 1.模型原理1.1 模型原理1.2 数学模型2.模型参数3.文件结构4.Excel数据5.下载地址6.完整代码7.运行结果1.模型原理 决策树是一种基于树状结构的分类和回归模型,它通过一系列的决策规则来将数据划分为不同的类…...

并发编程4:Java 中的并发基础构建模块
目录 1、同步容器类 1.1 - 同步容器类的问题 1.2 - 迭代和容器加锁 2、并发容器类 2.1 - ConcurrentHashMap 类 2.2 - CopyOnWriteArrayList 类 3、阻塞队列和生产者-消费者模式 3.1 - 串行线程封闭 4、阻塞方法与中断方法 5、同步工具类 5.1 - 闭锁 -> CountDow…...

Vue-10.集成(.editorconfig、.eslintrc.js、.prettierrc)
介绍 同时使用 .editorconfig、.prettierrc 和 .eslintrc.js 是很常见的做法,因为它们可以在不同层面上帮助确保代码的格式一致性和质量。这种组合可以在开发过程中提供全面的代码维护和质量保证。然而,这也可能增加一些复杂性,需要谨慎配置…...
PHP-FPM进程排查
1、查看php-fpm的进程个数 ps -ef |grep "php-fpm"|grep "pool"|wc -l2、查看每个php-fpm占用的内存大小 ps -ylC php-fpm --sort:rss3.查看PHP-FPM在你的机器上的平均内存占用 ps --no-headers -o "rss,cmd" -C php-fpm | awk { sum$1 } END…...

PHP-MD5注入
0x00 前言 有些零散的知识未曾关注过,偶然捡起反而更加欢喜。 0x01 md5 注入绕过 md5函数有两个参数,第一个参数是要进行md5的值,第二个值默认为false,如果为true则返回16位原始二进制格式的字符串。意思就是会将md5后的结果当…...
对redis、redisson、springcache总结
<一> redis-缓存中间件 什么是redis redis是c语言开发的,一个高性能key-value键值对内存数据库,可以用来做数据库、缓存、消息中间件的一种非关系型数据库。 redis数据存储在哪里 内存和磁盘中,但是redis的读写都在内存中,…...

Java基础知识实际应用(学生信息管理系统、猜拳小游戏、打印日历)
一、Java学生信息管理系统 这个系统包含了添加、修改、删除、查询和显示所有学生信息等功能。您可以在此基础上进行修改和完善,以适应您的需求。 import java.util.Scanner;public class StudentManagementSystem {private static Scanner scanner new Scanner(S…...

Git:在本地电脑上如何使用git?
git 版本: 2.40.1.windows.1 文章目录 一. 使用git之前你必须要理解的几个概念1.1 理解工作区、版本库、暂存区的概念1.2 提交Git版本库的步骤【分两步执行】 二. Git本地库实战2.1 初始化版本库2.2 新建 & 提交 & 状态2.3 查看日志2.4 回退 & 穿梭 &am…...
卷和分区的关系
1、分区 存储空间管理和仓库管理类似,只不过仓库管理的是货物,存储空间管理的是文件。当仓库规模小时,可以不划分货物的存放区域,但当仓库规模很大,就必须根据货物的类型和存储需要,把仓库分为多个区域。例…...

Linux下在qtcreator中创建qt程序
目录 1、新建项目 2、单工程项目创建 3、多工程项目创建 4、添加子工程(基于多工程目录结构) 5、 .pro文件 1、新建项目 切换到“编辑”界面,点击菜单栏中的“文件”-“新建文件或项目” 2、单工程项目创建 只有一个工程的项目&#…...

快递再多也不怕!你的顺丰快递用上5G“神器”
互联网时代,剁手党疯狂“买买买”之后,快递件量再创新高。《2023年6月中国快递发展指数报告》显示,2023二季度单月快递业务量稳定在百亿件以上。其中,由于“618”电商促销活动与父亲节叠加,6月16日至20日单日揽收量均超…...

微信小程序:模板使用
目录 模板的优点: 一、静态模板创建 二、静态模板使用 1.*.wxml引入模板 2.模板使用 3.*.wxss引入模板的样式 三、动态模板创建 四、动态模板使用 1.*.wxml引入模板 2.模板使用 3.*.js定义动态数据 五、结果展示 总结 模板的优点: 有利于保持网…...

AUTOSAR NvM Block的三种类型
Native NVRAM block Native block是最基础的NvM Block,可以用来存储一个数据,可以配置长度、CRC等。 Redundant NVRAM block Redundant block就是在Native block的基础上再加一个冗余块,当Native block失效(读取失败或CRC校验失…...

Vue+ElementUI实现选择指定行导出Excel
这里记录一下,今天写项目时 的一个需求,就是通过复选框选中指定行然后导出表格中选中行的Excel表格 然后这里介绍一个工具箱(模板):vue-element-admin 将它拉取后,运行就可以看到如下界面: 这里面的很多功能都已经实现…...

SNMP简单介绍
SNMP SNMP是广泛应用于TCP/IP网络的网络管理标准协议,该协议能够支持网络管理系统,用以监测连接到网络上的设备是否有任何引起管理上关注的情况。SNMP采用轮询机制,提供最基本的功能集,适合小型、快速、低价格的环境使用…...

使用python对图像加噪声
加上雨点噪声 import cv2 import numpy as npdef get_noise(img, value10):#生成噪声图像>>> 输入: img图像value 大小控制雨滴的多少 >>> 返回图像大小的模糊噪声图像noise np.random.uniform(0, 256, img.shape[0:2])# 控制噪声水平ÿ…...

以 Java NIO 的角度理解 Netty
文章目录 前言Java NIO 工作原理Selector 的创建ServerSocketChannel 的创建ServerSocketChannel 注册 Selector对事件的处理总结 前言 上篇文章《Netty 入门指南》主要涵盖了 Netty 的入门知识,包括 Netty 的发展历程、核心功能与组件,并且通过实例演示…...

Maven自定义脚手架(多module模块)+自定义参数
脚手架 视频教程: Maven保姆级教程 脚手架是一个项目模板,包含常用的工程结构、代码。 1 自定义脚手架 脚手架创建的步骤如下,先创建一个工程,把常用的代码写好,进入工程根目录,进行如下操作: …...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...

tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》
近日,嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》,海云安高敏捷信创白盒(SCAP)成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天,网络安全已成为企业生存与发展的核心基石,为了解…...
从实验室到产业:IndexTTS 在六大核心场景的落地实践
一、内容创作:重构数字内容生产范式 在短视频创作领域,IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色,生成的 “各位吴彦祖们大家好” 语音相似度达 97%,单条视频播放量突破百万…...

【Axure高保真原型】图片列表添加和删除图片
今天和大家分享图片列表添加和删除图片的原型模板,效果包括: 点击图片列表的加号可以显示图片选择器,选择里面的图片; 选择图片后点击添加按钮,可以将该图片添加到图片列表; 鼠标移入图片列表的图片&…...

MTK-Android12-13 Camera2 设置默认视频画质功能实现
MTK-Android12-13 Camera2 设置默认视频画质功能实现 场景:部分客户使用自己的mipi相机安装到我们主板上,最大分辨率为1280720,但是视频画质默认的是640480。实际场景中,在默认视频分辨率情况下拍出来的视频比较模糊、预览也不清晰…...