操作系统——信号

将信号分为以上四个阶段

1.信号注册:是针对信号处理方式的规定,进程收到信号时有三种处理方式:默认动作,忽略,自定义动作。如果不是自定义动作,这一步可以忽略。这个步骤要使用到signal/sigaction接口

2.信号产生:就是操作系统向进程发出信号 

3.信号保存

4.信号捕捉处理


信号有哪些

1-31是普通信号 34-62是实时信号 

Action列指的是当信号被发送到一个进程时,默认操作系统采取的动作。具体的动作类型和含义如下:

  1. Term (Terminate)

    • 终止进程。此操作表示操作系统将结束进程的执行。这是大多数信号的默认动作。
  2. Core (Terminate and Dump Core)

    • 终止进程并生成核心转储文件。核心转储文件包含了进程在被终止时的内存状态,可以用于调试目的。想了解core的调试看这篇文章
  3. Ign (Ignore)

    • 忽略信号。进程接收到信号时,操作系统不会采取任何动作,也不会通知进程。
  4. Stop

    • 停止进程的执行。进程被暂停,直到接收到继续信号(如SIGCONT)。
  5. 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系统上都可 用。请编写程序验证这样做不会产生僵尸进程。

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

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

相关文章

5. 条件和递归

5. 条件和递归 本章主要话题是if表达式, 它根据程序的状态执行不同的代码. 但首先介绍两个操作符号: 向下取整除法操作符和求模操作符.5.1 向下取整除法操作符和求模操作符 向下取整除法操作符(//)对两个数除法运算, 并向下取整得到一个整数. 假设, 一个电影的播放时长为105分…

Java | Leetcode Java题解之第150题逆波兰表达式求值

题目&#xff1a; 题解&#xff1a; class Solution {public int evalRPN(String[] tokens) {int n tokens.length;int[] stack new int[(n 1) / 2];int index -1;for (int i 0; i < n; i) {String token tokens[i];switch (token) {case "":index--;stack…

vi/vim使用命令

你是否在编辑文件时以为键盘坏了&#xff0c;为什么不能删除呢&#xff0c;为什么不能敲代码呢&#xff0c;等你初识vi&#xff0c;会觉得这个东西为什么设计得这么难用&#xff0c;这篇教程带你熟练得用上这款经典的工具 Vi 是在 Unix 系统上广泛使用的编辑器&#xff0c;Vim …

Unity与Js通信交互

目录 1.Js给Unity传递消息 2.Unity给Js传递消息 简介: Unity 与 JavaScript 通信交互是指在 Unity 项目中实现与 JavaScript 代码进行数据交换和功能调用的过程。 在 Unity 中&#xff0c;可以通过特定的接口和技术来与外部的 JavaScript 环境进行连接。这使得 Unity 能够利…

进击算法工程师深度学习课程

"进击算法工程师深度学习课程"旨在培养学员在深度学习领域的专业技能和实战经验。课程涵盖深度学习基础理论、神经网络架构、模型优化方法等内容&#xff0c;通过项目实践和算法实现&#xff0c;帮助学员掌握深度学习算法原理和应用&#xff0c;提升在算法工程师领域…

EasyExcel文件导出,出现有文件但没有数据的问题

一开始由于JDK版本过高&#xff0c;我用的17&#xff0c;一直excel没有数据&#xff0c;表头也没有&#xff0c;后来摸索了好久&#xff0c;找了资料也没有&#xff0c;后来改了代码后报了一个错误&#xff08;com.alibaba.excel.exception.ExcelGenerateException: java.lang.…

【抽代复习笔记】19-群(十三):奇偶置换、循环置换的几个定理及例题

定义&#xff1a; ①在Sn中&#xff0c;能够表示为奇数多个对换乘积的置换称为“奇置换”&#xff0c;能够表示为偶数多个对换乘积的置换称为“偶置换”&#xff1b; ②所有偶置换的集合记为An。 例1&#xff1a;&#xff08;1&#xff09;计算S1和S2中奇、偶置换的数目&…

数据中台-知识图谱平台

【数据分析小兵】专注数据中台产品领域,覆盖开发套件,包含数据集成、数据建模、数据开发、数据服务、数据可视化、数据治理相关产品以及相关行业的技术方案的分享。对数据中台产品想要体验、做二次开发、关注方案资料、做技术交流的朋友们&#xff0c;可以关注我。 1. 概述 随着…

ResNet——Deep Residual Learning for Image Recognition(论文阅读)

1.什么是ResNet ResNet是一种残差网络&#xff0c;咱们可以把它理解为一个子网络&#xff0c;这个子网络经过堆叠可以构成一个很深的网络。下面是ResNet的结构。 2.为什么要引入ResNet 理论上来说&#xff0c;堆叠神经网络的层数应该可以提升模型的精度。但是现实中真的是这…

智慧守护 畅游无忧——北斗应急呼叫柱,为景区安全加码

在大自然的怀抱中&#xff0c;中型及大型公园、景区以其壮丽风光吸引着成千上万的游客前来探索&#xff0c;成为了人们休闲娱乐的好去处。然而&#xff0c;广袤的区域、复杂的地形和分散的人流也给安全保障带来了前所未有的挑战。传统的巡逻方式难以覆盖每一个角落&#xff0c;…

【第六篇】SpringSecurity的权限管理

一、权限管理的实现 服务端的各种资源要被SpringSecurity的权限管理控制可以通过注解和标签两种方式来处理。 放开了相关的注解后在Controller中就可以使用相关的注解来控制了 JSR250注解 /*** JSR250*/ @Controller @RequestMapping("/user") public class UserC…

物理隔离后数据怎么导入和导出?安全U盘一键解决

政府单位、军工和科研所、航空航天企业、金融机构、医疗单位、电力企业、生物制药实验室等企业及单位&#xff0c;因研发和生产过程、或日常经营中涉及大量敏感信息和技术&#xff0c;需要通过物理隔离来确保网络的安全性。因此&#xff0c;多采用物理隔离的方式进行网络建设。…

前端 CSS 经典:在 Vue3 中使用渐进式图片

1. 什么是渐进式图片 当我们网站会加载很多图片的时候&#xff0c;有些图片尺寸很大&#xff0c;加载就会很慢&#xff0c;会导致页面长时间陷入白屏状态&#xff0c;用户体验很不好。所以可以使用渐进式图片&#xff0c;先给用户展示模糊图&#xff0c;这些图尺寸小&#xff…

django学习入门系列之第二点《浏览器能识别的标签3》

文章目录 列表表格往期回顾 列表 无序列表 <!-- <ul </ul> 无序列表 --> <ul><li> 内容1 </li><li> 内容2 </li><li> 内容3 </li><li> 内容4 </li> </ul>有序列表 <!-- <ol> &…

WordPress、Typecho 站点如何让 CloudFlare 缓存加速

众所周知 WordPress、Typecho 都是著名动态博客站点(一个最简单的判断依据就是都要依赖结合数据库),这类站点在 CDN 缓存上都有一个致命的缓存弊端就是动静态请求的区分,理论上要让 CDN 绕过所有的动态请求,缓存所有的静态请求,否则就会造成前端登录和非登录状态的混乱,…

小程序制作成本是多少?揭秘隐藏费用!

在日常生活中&#xff0c;有很多小程序&#xff0c;其中我们最熟悉的是微信小程序&#xff0c;所以小程序对我们每个人来说并不陌生。那么&#xff0c;你知道制作一个小程序要花多少钱吗&#xff1f;制作小程序的成本是多少&#xff1f;今天&#xff0c;本文将带领您揭示小程序…

禁渔期水域监管:EasyCVR视频智能监控方案

一、背景与需求分析 根据农业部印发的《中国渔政亮剑2024系列专项执法行动方案》&#xff0c;我国将持续推进长江十年禁渔、海洋伏季休渔、黄河等内陆重点水域禁渔等专项行动。根据四川省相关规定&#xff0c;每年3月1日至6月30日为禁渔期&#xff0c;在此期间&#xff0c;四川…

颠覆与创新:探寻Facebook未来的发展路径

Facebook&#xff0c;这个曾经引领社交网络革命的巨头&#xff0c;在如今竞争激烈的科技市场中&#xff0c;正面临着前所未有的挑战和机遇。如何在不断变化的数字世界中保持竞争力&#xff0c;成为业界领先者&#xff0c;这是摆在Facebook面前的重要课题。本文将探寻Facebook未…

单位经常要你加班却不发加班费,你想到这一招没有?

单位经常要你加班却不发加班费&#xff0c;你想到这一招没有&#xff1f; 你也许经常在忙碌的工作和繁重的加班中度过&#xff0c;然而&#xff0c;却从未得到过应有的加班费。但你又不想离开这个单位&#xff0c;或许单位的工作环境人性化&#xff0c;同事之间的关系融洽&…

【新手必看】修复Windows11蓝牙连接问题的7个方法!

在Windows11电脑操作中&#xff0c;用户经常会到蓝牙功能&#xff0c;如果蓝牙连接出现问题了&#xff0c;就会导致用户无法成功使用蓝牙。但是&#xff0c;许多新手用户不清楚要怎么操作才能解决蓝牙连接问题&#xff1f;会有不同的因素导致蓝牙连接出现问题&#xff0c;接下来…