操作系统——信号
将信号分为以上四个阶段
1.信号注册:是针对信号处理方式的规定,进程收到信号时有三种处理方式:默认动作,忽略,自定义动作。如果不是自定义动作,这一步可以忽略。这个步骤要使用到signal/sigaction接口
2.信号产生:就是操作系统向进程发出信号
3.信号保存
4.信号捕捉处理
信号有哪些
1-31是普通信号 34-62是实时信号
Action列指的是当信号被发送到一个进程时,默认操作系统采取的动作。具体的动作类型和含义如下:
-
Term (Terminate):
- 终止进程。此操作表示操作系统将结束进程的执行。这是大多数信号的默认动作。
-
Core (Terminate and Dump Core):
- 终止进程并生成核心转储文件。核心转储文件包含了进程在被终止时的内存状态,可以用于调试目的。想了解core的调试看这篇文章
-
Ign (Ignore):
- 忽略信号。进程接收到信号时,操作系统不会采取任何动作,也不会通知进程。
-
Stop:
- 停止进程的执行。进程被暂停,直到接收到继续信号(如SIGCONT)。
-
Cont (Continue):
- 继续执行被停止的进程。此操作恢复一个之前被暂停的进程的执行。
理解信号
信号和生活中的信号是一样的。例如下课铃声就是一个信号,上学的第一天,老师会告诉我们下课铃声响起的时候就可以下课休息——信号规定。当一节课的下课铃声响起,我们收到这个信号,但是老师想拖堂,我们先将这个信号保存到大脑,等老师讲完才会对下课信号处理。从下课铃声响起到真正下课这段时间就是时间窗口。信号产生了并不代表现在就要处理,进程会选择在合适的时间进行处理。
信号注册
signal
signal是将signum这个信号的处理方式进行自定义
注意:信号9和信号19不可以修改,因为进程终止和停止的权利必须由操作系统掌握
例子:
将信号1自定义捕捉
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>using namespace std;
void fun(int signum)
{cout << "get signum" << signum << endl;
}int main()
{signal(1,fun);while(1){cout << "process running pid:" << getpid() << endl;sleep(1);}return 0;
}
运行程序发送信号1
sigaction
了解信号保存信号处理后再了解这个接口!!!!
sigaction结构体中,第一个是自定义动作函数指针,第三个是处理信号时要屏蔽的信号,其他的暂时不考虑。
act表示信号处理的方式,oact表示之前信号处理的方式。
例子:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>using namespace std;void PrintPend(sigset_t& set)
{for(int signo = 31; signo >= 1; signo--){if(sigismember(&set, signo)) cout << 1;else cout << 0;}cout << endl;
}
void header(int sig)
{sigset_t set;sigemptyset(&set);while(1){sigpending(&set);PrintPend(set);sleep(1);}
}
int main()
{struct sigaction act,oact;sigaddset(&act.sa_mask, 3);sigaddset(&act.sa_mask, 4);sigaddset(&act.sa_mask, 5);act.sa_handler = header;sigaction(SIGINT, &act, &oact);while(1){cout << "process running:" << getpid() << endl;sleep(1);}return 0;
}
3 4 5 信号都被屏蔽了,处理信号的过程发送信号只会先保存
当操作系统处理信号调用自定义动作时先将对应信号pend置为0,为了防止信号的嵌套处理,还会自动将当前信号屏蔽。
sa_mask可以自己设置要屏蔽的信号
信号发送
什么是信号发送
信号是由OS向进程发送的,信号就一定保存在进程中。普通信号有31个,以位图的形式储存到进程PCB的一个int类型中。实时信号与普通信号的区别就是:实时信号收到后必须立即处理不会等待,实时信号是存储在进程的一个队列中。所以发信号就是操作系统修改对应的int值或者队列
信号发送方式
键盘组合键
例如:
ctrl+c,信号2中断进程
ctrl+\,信号3退出进程
键盘组合键是怎么发出信号的呢?
原理
键盘写入完毕后,会向CPU发送硬件中断包括中断号,CPU告诉操作系统,操作系统通过中断号到中断向量表寻找中断号所对应的方法地址,使用该方法将键盘缓冲区的数据写到OS缓冲区,操作系统拿到数据后对进程发出信号
另外,键盘只能向前台进程(哪个进程能获取键盘输入,哪个进程就是前台进程)发送信号。Linux中一个登录只能有一个前台进程,可以有多个后台进程。
当我们./运行一个程序时,前台进程就是正在运行的程序,ctrl+c就会终止当前进程。
如果在运行时./后面加上&,当前进程就会以后台进程的方式运行,ctrl+c无效。因为前台进程是bash,此时键盘任何输入都会给bash,也就意味着这时可以使用命令行
kill命令
kill signum PID
系统调用接口
kill
向其他进程发送信号
样例:
写一个可以给其他进程发信号的程序
//myprocess.cc
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{while(1){cout << "process: " << getpid() << " running" << endl;sleep(1);}return 0;
}//mykill.cc
#include <iostream>
#include <sys/types.h>
#include <signal.h>using namespace std;
void Usage(const char* argv)
{cout << argv << " pid " << "sig" << endl;
}int main(int argc, const char* argv[])
{if(argc != 3){Usage(argv[0]);}else{int n = kill(stoi(argv[1]), stoi(argv[2]));if(n == -1){perror("kill fail");return -1;}}return 0;
}
raise
向当前进程发送信号
实际上调用了kill接口,相当于kill(getpid(), sig)
abort
让当前进程终止
实际上调用了kill接口,相当于kill(getpid(), 6)
注意:信号6是由其他进程发来的,不会让进程退出
alarm
在设定时间过后,发送信号
返回前一个定时器的剩余时间(以秒为单位),如果之前没有设置定时器,则返回0
#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;
void fun(int sig)
{int n = alarm(5);cout << "get alarm" << "time:" << n <<endl;
}int main()
{//alarm收到信号后默认退出进程,进行自定义信号捕捉signal(SIGALRM, fun);alarm(5);while(1){cout << "process running" << endl;sleep(1);}
}
异常
例如遇到除0错误时,CPU在运算的过程中出现错误,会将这个情况告诉操作系统,再由操作系统给进程发信号,中断进程。操作系统即是硬件设备的管理者也是进程的管理者
如果将这个信号自定义捕捉,并且捕捉的动作不会让进程退出,会怎么样呢?
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>using namespace std;
void fun(int signum)
{cout << "get signum" << signum << endl;sleep(1);
}int main()
{signal(8,fun);int a= 1/0;return 0;
}
操作系统会一直给进程发信号。因为进程收到信号未关闭,进程会一直被CPU调度运行,一直出现错误。
信号保存
信号有几种状态:
递达(delivery):实际执行信号的处理动作
未决(panding):从信号被发出到递达之间的状态
阻塞(block):进程可以阻塞某个信号,当该信号别发出时,不会递达,只有当信号解除阻塞时才会递达
阻塞和忽略不同,忽略是递达后的处理方式
信号保存主要就是通过阻塞实现的
信号在内核中的表示:
block位图表示信号是否被阻塞,pending位图表示信号是否发出,handler是函数指针数组,存储了信号的处理方法,SIG_DFL是默认方法,SIG_IGN是忽略,还可以指向用户区自己定义的方法。
操作系统提供了block(阻塞信号集/信号屏蔽字)和pending(未决信号集)的数据类型sigset_t还有相应的系统调用接口
信号集操作接口
sigemptyset将所有标志位都置为0,sigfillset将所有标志位都置为1
sigaddset/sigdelset :增加/删除signum信号所对应的位置
sigismember检测signum在set中是否为1
修改屏蔽信号字接口
how可以以下有几个值:
SIG_BLOCK:set中包含了希望添加到当前屏蔽信号字的信号
SIG_UNBLOCK:set包含了希望从当前屏蔽信号字删除的信号
SIG_SETMASK:将屏蔽信号字设置为set
set就是用来更改信号屏蔽字的屏蔽字参数,oset用来存储更改信号屏蔽字之前的屏蔽字参数
显示未决信号集的接口
将未决信号集拷贝到set
实例
#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;void hander(int sig)
{cout << "get signal:" << sig << endl;
}int main()
{signal(2,hander);sigset_t set, oset;sigemptyset(&set);sigemptyset(&oset);sigaddset(&set, SIGINT);//设置屏蔽信号字sigprocmask(SIG_BLOCK, &set, &oset);int cnt = 5;while (1){sigset_t pending;sigpending(&pending);//展示未决信号集for (int i = 31; i >= 1; i--){if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;sleep(3);cnt--;if(cnt == 0)//解除屏蔽信号字{sigprocmask(SIG_SETMASK, &oset, nullptr);}}return 0;
}
注意,和信号捕捉一样,信号9和信号19不可以被阻塞
信号处理
什么时候处理
结论:当进程从内核态变为用户态,操作系统会进行信号的检测和处理。
内核态:进程访问操作系统的代码和数据
用户态:进程访问自己的代码和数据
CPU中有一些寄存器的标志位可以区分进程在哪个态。有几个进程就有几个用户级页表,而内核级页表只有一个,不管进程怎么切换,每个进程看到的内核空间都是一样的。从进程的角度看,调用系统调用接口,就是在自己的进程地址空间调用。从操作系统的角度看,在任意时刻,只要有进程运行就可以随时调用系统调用接口。
怎么处理
当进程进入内核态(例如调用了系统调用接口),在执行系统调用操作后,会检查是否有可以递送的信号并进行处理然后返回用户态,如果是处理自定义的动作信号,就会先从内核进入用户态(因为用户态下,处理函数做非法操作会被操作系统拦截,保证了安全性),调用信号处理函数,再回到内核态,最后返回用户态,从主控制流程上次中断的地方继续执行
信号与进程等待
子进程退出会向父进程发送信号SIGCHLD,不过这个信号默认处理方式时忽略的,可以通过自定义捕捉对进程回收。
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>using namespace std;
void header(int sig)
{pid_t rid;while((rid = waitpid(-1,nullptr,WNOHANG)) > 0){cout << "wait :" << rid << " success" << endl;}
}int main()
{srand(time(nullptr));signal(SIGCHLD, header);// 创建10个子进程for (int i = 10; i > 0; i--){pid_t id = fork();if (id == 0){cout << "I am child:" << getpid() << endl;sleep(rand() % 2 + 1);cout << "child quit:" << getpid() << endl;sleep(rand() % 2 + 1);exit(0);}sleep(rand() % 3 + 3);}while(1){cout << "I am father:" << getpid() << endl;sleep(1);}
}
事实上,由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调 用sigaction将SIGCHLD的处理动作 置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不 会产生僵尸进程,也不会通知父进程。系统默认的忽 略动作和用户用sigaction函数自定义的忽略 通常是没有区别的,但这是一个特例。此方法对于Linux可用,但不保证 在其它UNIX系统上都可 用。请编写程序验证这样做不会产生僵尸进程。
相关文章:

操作系统——信号
将信号分为以上四个阶段 1.信号注册:是针对信号处理方式的规定,进程收到信号时有三种处理方式:默认动作,忽略,自定义动作。如果不是自定义动作,这一步可以忽略。这个步骤要使用到signal/sigaction接口 2.…...
力扣1482.制作m束花所需的最少时间
力扣1482.制作m束花所需的最少时间 二分答案 check的时候 用一个bool数组判断是否开花找连续的k朵花 const int N 1e510;int st[N];class Solution {public:int minDays(vector<int>& bloomDay, int m, int k) {int n bloomDay.size();if(n < (long long)m*…...
解决 Linux 和 Java 1.8 中上传中文名称图片报错问题
在 Linux 系统和 Java 1.8 中,当尝试上传含有中文名称的图片时,可能会遇到以下错误提示: Caused by: java.nio.file.InvalidPathException: Malformed input or input contains unmappable characters: /home/uploadPath/2024/06/12/扣子蝴蝶…...

cocos开发的时候 wx.onShow在vscode里面显示红色
这个函数是在微信小游戏平台才会用到。 cocos识别不到wx这个变量。 可以改成下面的写法。 只要在变量前面加一个globalThis.就能识别这个变量了。也不报错了。 搞死强迫症了。orz 欢迎大家来玩我的微信小游戏。多多提意见啊。...

使用 PNPM 从零搭建 Monorepo,测试组件并发布
1 目标 通过 PNPM 创建一个 monorepo(多个项目在一个代码仓库)项目,形成一个通用的仓库模板。 这里以在该 monorepo 项目中搭建 web components 类型的组件库为例,介绍从仓库搭建、组件测试到组件发布的整个流程。 这个仓库既可…...
Oracle 19C 数据库表被误删除的模拟恢复
Oracle 19C 数据库表被误删除的模拟恢复操作 1、模拟创建表用于恢复测试 sqlplus zzh/zzh SQL> create table obj_tb tablespace users as select * from dba_objects; Table created. SQL> select count(*) from obj_tb; COUNT(*) ---------- 72373 2、记录当前…...

【CICID】GitHub-Actions语法
[TOC] 【CICID】GitHub-Actions语法 1 场景 当我们开发过程中,经常需要提交代码,打包,部署新代码到对应的环境,整个过程都是人工手动操作,占据开发人员大量时间,并且很繁琐容易出错。所以需要借助一些…...
Ionic 创建 APP
Ionic 创建 APP Ionic 是一个强大的开源框架,用于构建高性能、高质量的移动和网页应用程序。它结合了 Angular、React 或 Vue 的强大功能,以及 Capacitor 或 Cordova 的原生功能,使得开发者可以轻松地创建跨平台的应用程序。本篇文章将指导您如何使用 Ionic 创建一个基本的…...

【数学代码】幂
Hello!大家好,我是学霸小羊,今天来讲讲幂。 求几个相同因数的积的运算,叫做乘方,乘方的结果叫做幂。 a^n,读作 “ a的n次方 ” 或 “ a的n次方幂”,a叫做底数,n叫做指数。 对于底数、指数和幂…...
os.system() 函数
os.system() 是 Python 标准库 os 模块中的一个函数,用于在子终端中运行系统命令。它可以在 Python 脚本中调用外部命令或程序。具体来说,它通过执行命令字符串并返回执行状态来实现这一点。下面是对 os.system() 函数的详细解释: import os…...
Spring Boot中的RESTful API详细介绍及使用
在Spring Boot中,RESTful API的实现通过控制器类中的方法和特定的注解来完成。每个注解对应不同的HTTP请求方法,并通过处理请求参数和返回响应来实现不同的操作。 下面将详细解释RESTful API中的各个方面,包括GetMapping, PostMapping, PutMa…...

nlp学习笔记
目录 很多入门例子 bert chinese 很多入门例子 https://github.com/lansinuote/Huggingface_Toturials bert chinese import torch import torch.nn as nn from transformers import AutoTokenizer, AutoModel, BertModel, TFBertModel, BertTokenizer# youpath = D:/bert-…...
使用python获取内存信息
#!/usr/bin/python # -*- coding:utf-8 -*- psutil模块是一个跨平台的获取进程和系统应用情况(CPU,内存,磁盘,网络,传感器)的库。 该模块用于系统监控、限制进程资源和运行进程的管理等方面。 内存信息&am…...
外包公司泛滥,这些常识你应该提前知道?
今年大环境确实很不好 很多985,211的应届生都在网上大吐苦水,很多大龄离职大厂的技术人也好,业务人也好,都纷纷转向短视频平台做起了自媒体。而找工作的人普遍发现,某最火的招聘平台几乎都被外包公司刷屏了。大大小小的外包公司如…...
Linux下的抓包工具使用介绍
应用层 传输层 网络层 数据链路层 物理层 1)tcpdump(传输/网络层) tcpdump -i eth0 tcpdump -i eth0 -vnn -v:显示包含有TTL,TOS值等等更详细的信息 -n:不要做IP解析为主机名 -nn:…...

centos环境上:k8s 简单安装教程
本次演示安装3节点k8s环境,无需多言,直接上操作步骤: 1、环境准备 k8s部署前,首先需要准备好环境,除了1.4 步骤,其他步骤在所有(3个)节点上都要执行: 1.1 关闭防火墙 s…...

短视频矩阵系统/源码搭建---拆解热门视频功能开发上线
短视频矩阵系统/源码搭建 一、短视频矩阵系统源码开发需要用到以下技术: 1.前端技术:HTML、CSS、JavaScript、Vue.js等前端框架。 2.后端技术:Java、Python、PHP等后端语言及相关框架,如Spring Boot、Django、Laravel等。 3.移…...

手机和模拟器的 Frida 环境配置
目录 一、配置 JDK 和 android 环境 二、连接设备和查看权限 1、连接设备 2、查看手机权限 三、手机配置 Frida 1、frida-server下载 2、验证 四、模拟器配置 Frida 1、下载模拟器并调节成手机版: 2、连接并查看架构 3、配置并开启 x86 的 frida-serve…...
力扣1385.两个数组间的距离值
力扣1385.两个数组间的距离值 二分判断答案是否正确 class Solution {public:int findTheDistanceValue(vector<int>& arr1, vector<int>& arr2, int d) {ranges::sort(arr2);ranges::sort(arr1);int m arr2.size();auto check [&](int low,int h…...
[C++] 小游戏 斗破苍穹 2.11.6 版本 zty出品
大家好,今天zty带来的是斗破苍穹的 2.11.6 版本,这个版本主要更新了:1、背包 2、将退出游戏改到了设置里面 3、如果不逃跑不会停止战斗。废话不多说, 先赞后看 养成习惯 code #include<stdio.h> #include<iostrea…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...