操作系统(信号处理)
一、信号介绍
什么是中断:
-
当进程接收到消息后中止当前正在执行的任务,转而执行其它任务,等待其它任务执行完毕后再返回继续执行。这种执行模式称为中断,分为硬件中断和软件中断两种
什么是信号:
-
信号是UNIX、类UNIX以及其他POSIX兼容的系统中,为了完成不同进程之间通讯的一种方式。是一种软中断,是一种异步处理机制,用于提醒进程某个事件发生了,可能要去处理。
-
当一个信号发送给一个进程,操作系统就会中断该进程的正常控制流程,如果中断前预先设置过如何处理该信号(绑定过该信号的信号处理函数),那么中断后会执行该函数,执行完返回中断点继续执行,否则就按信号的默认处理方式进行
# 执行 kill -l 查看当前系统可以产生的所有信号 常见的信号: SIGINT(2) Ctrl+c 终止 SIGQUIT(3) Ctrl+\ 终止+core SIGFPE(8) 除0 终止+core SIGSEGV(11) 段错误 终止+core SIGTSTP(20) Ctrl+z 暂停 SIGCONT(18) fg 继续
不可靠信号与可靠信号
不可靠信号:
-
建立在早期信号处理机制上的信号被称为不可靠信号(1-31)
-
非实时信号,不支持排队机制,可能会丢失信号,同一个信号产生多次,进程可能只收到一次
-
当进程收到这类信号,执行了第一次用户设置的信号处理函数后,会还原会默认的处理方式。
可靠信号:
-
位于(34-64)的信号是可靠信号
-
实时信号,支持排队机制,信号只要产生就必定会被处理,因此不会丢失
信号的来源:
-
硬件异常:由硬件设备产生的信号,例如除零、非法访问内存、总线错误、未定义指令
-
软件异常:命令kill\函数产生信号
信号处理的默认动作:
-
忽略
-
终止进程
-
终止并产生core文件
-
捕获并处理
二、捕获信号
#include <signal.h> // 信号处理函数的格式 typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 功能:向内核注册一个信号和它的信号处理函数,相当于捕获该信号(绑定信号) signum:信号码(可以写整数值也可以用宏名)
-
在某些UNIX系统中,通过signal注册信号处理函数后,只有一次有效,后面会变回默认处理方式,为了在这种系统中得到持久的信号绑定,可以在信号处理函数的末尾再次通过signal重新注册一次
-
可以通过命令 kill 信号码 进程号 给该进程发送信号
练习:测试一下哪些信号不可以被捕获处理
9和19号信号不能被忽略也不能被不能被捕获
-
普通用户只能给自己的进程发送信号,只有root用户可以给任何进程发送信号
-
当信号处理完信号处理函数后是会回到产生信号的代码位置继续执行,如果我们捕获并处理的是段错误\除零这种信号,就会产生死循环,因为这些错误并没有因为捕获了信号就消失,而是一直存在并产生信号,正确做法是在他们的信号处理函数中进行数据保存然后直接结束程序。exit()
三、发送信号的方式
键盘:
-
给当前终端控制下的活跃进程发送信号
-
Ctrl+c Ctrl+\ Ctrl+z fg
-
命令:
kill <-信号码> 进程号 killall <-信号码> 进程名给所有的同名的进程发送信号
事件:
-
当程序执行了某种非法操作,被操作系统发现了,操作系统会给进程发送对应的信号,例如段错误、除零、总线错误、错误指令等。
函数:
int kill(pid_t pid, int sig); 功能:给指定的进程发送信号 pid:给进程号为pid的进程发送pid > 0 给pid号进程发送信号sigpid = 0 给同组的所有进程发送信号sigpid = -1 给所有进程发送信号sig,前提是有向该进程发送信号的权限pid < -1 向进程组id等于pid的绝对值的所有进程发送信号int raise(int sig); 功能:给调用进程自己发送信号 void abort(void); 功能:给调用进程自己发送信号SIGABRT(6)unsigned int alarm(unsigned int seconds); 功能:让内核在seconds秒后,向调用进程发送SIGALRM(14)信号 返回值:上一次alarm剩余的时间如果是正常走完时间返回0,如果时间还没走完,又再次调用alarm后,会覆盖之前的时间重新计时,并不会产生多个闹钟信号
四、暂停和休眠
int pause(void); 功能:让调用进程进入暂停态执行,进入睡眠状态,直到有信号终止进程或者有信号被捕获,会唤醒并执行信号处理函数,继续后面的执行流程。类似于不限时的sleep 返回值:要么一直睡眠不返回,睡醒返回-1
unsigned int sleep(unsigned int seconds); 功能:让调用进程睡眠seconds秒,除非有信号终止进程或者有信号被捕获,也会唤醒 返回值:剩余的睡眠秒数 int usleep(useconds_t usec) 功能:让调用进程睡眠usec微秒,除非有信号终止进程或者有信号被捕获,也会唤醒 返回值:剩余的睡眠秒数
五、信号集与信号屏蔽
什么是信号集:
-
是一种专门用于存储多个信号的数据类型 sigset_t
-
该类型占128字节,每个字节代表了一种信号的有或无
操作信号集的相关函数:
int sigemptyset(sigset_t *set); 功能:将信号集set中的所有信号置零 清空信号集 int sigfillset(sigset_t *set); 功能:把信号集set中所有信号置1 int sigaddset(sigset_t *set, int signum); 功能:将信号集set中的信号signum置1 int sigdelset(sigset_t *set, int signum); 功能:将信号集set中的信号signum置0 int sigismember(const sigset_t *set, int signum); 功能:测试信号集中是否存在signum信号 返回值:存在返回1, 不存在返回0 非法信号返回-1
#include <stdio.h>
#include <signal.h>
int main(int argc,const char* argv[])
{sigset_t set;sigfillset(&set);sigemptyset(&set);
sigaddset(&set,2);sigaddset(&set,7);
for(int i=1; i<=128; i++) { printf("信号%d 状态:%d\n",i,sigismember(&set,i));}
}
信号的递送与未决:
-
当信号产生后,系统内核会在其内部维护的进程表中,给响应信号的进程设置一个对应的标志位,着整个过程称为信号的递送
-
在信号产生到完成递送之间会存在一段时间间隔,处于这个时间间隔的信号状态是“未决”
信号屏蔽:
-
每个进程都有用一个信号掩码(signal mask,就是一个信号集),其中存在的信号是需要被该进程屏蔽的信号
-
让需要屏蔽的信号处于“未决状态”,当可以接收信号时,让其退出未决状态,完成递送
-
当执行一些特殊的且不想被干扰中断的操作时,例如:更新数据库敏感操作,此时可以把信号放入信号屏蔽集中,等操作完成后,再从信号屏蔽集中删除,继续处理信号,能保证敏感操作的安全性
// 信号屏蔽集的操作函数 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 功能:修改当前进程的信号掩码(屏蔽集) how:修改信号掩码的方式:SIG_BLOCK 将set中的信号加入到信号掩码中SIG_UNBLOCK 从信号掩码中把set中的信号删除SIG_SETMASK 把set中的信号替换掉信号掩码的所有信号 set:信号集 用于设置 oldsel:信号集 用于获取旧信号集 NULL则不获取
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigint(int num)
{ printf("按下了Ctrl+C\n");
}
void sigrtmin(int num)
{printf("我可靠!\n");
}
int main(int argc,const char* argv[])
{signal(SIGINT,sigint);signal(34,sigrtmin);
sigset_t set,old_set;sigemptyset(&set);
// 给信号集添加信号sigaddset(&set,SIGINT);sigaddset(&set,34);// 设置信号屏蔽sigprocmask(SIG_BLOCK,&set,&old_set);printf("我是进程%u\n",getpid());sleep(15);printf("我醒了,解除屏蔽!\n");// 还原屏蔽,解除屏蔽sigprocmask(SIG_SETMASK,&old_set,NULL);for(;;);
}
对于可靠和不可靠信号屏蔽的区别:
-
对于不可靠信号,通过信号屏蔽该信号后,在信号屏蔽期间,该信号产生多次,都只会被屏蔽第一个,只有第一个处于未决,剩余的都不参与排队直接忽略,当解除屏蔽后,只会有第一个不可靠信号被完成递送
-
相反,对于所有在屏蔽期间产生的可靠信号,都会排队变成未决,当屏蔽接触后,会按照次序全部完成递送
六、带参的信号发送与捕获
-
可以让不同进程之间发送信号时,带上一些简易数据,完成不同进程之间简单通信,这叫带参数的信号发送
#include <signal.h>
int sigaction (int signum, const struct sigaction* act,struct sigaction* oldact);
功能:设置信号的处理方案
signum:信号码
act:信号处理方式
oldact:原信号处理方式,可为NULL
struct sigaction {void (*sa_handler)(int); // 信号处理函数指针1void (*sa_sigaction)(int,siginfo_t*,void*); // 信号处理函数指针2 带附加数据sigset_t sa_mask; // 信号掩码//在信号函数函数执行过程中,默认屏蔽当前信号,如果想屏蔽其它信号可以向sa_mask中添加int sa_flags; // 信号处理标志void (*sa_restorer)(void); // 保留,NULL
};
sa_flags可为以下值的位或:SA_ONESHOT/SA_RESETHAND 执行完一次信号处理后,即对此信号的处理恢复为默认,这也是老版本signal函数的缺省行为。SA_NODEFER/SA_NOMASK 在信号处理函数的执行过程中,不屏蔽这个正在被处理的信号。SA_NOCLDSTOP 若signum参数取SIGCHLD,则当子进程暂停时,不通知父进程。SA_RESTART 系统调用一旦被signum参数所表示的信号中断,会自行重启。SA_SIGINFO 使用信号处理函数指针2,通过该函数的第二个参数,提供更多信息。
typedef struct siginfo {pid_t si_pid; // 发送信号的PIDsigval_t si_value; // 信号附加值(需要配合sigqueue函数)...
}siginfo_t;
typedef union sigval {int sival_int; 整数void* sival_ptr; 指针
}sigval_t;
int sigqueue (pid_t pid, int sig,const union sigval value);
功能:向pid进程发送sig信号,附加value值(整数或指针)。
在默认情况下,在信号处理函数的执行过程中,会自动屏蔽这个正在被处理的信号,而对于其它信号则不会屏蔽,通过sigaction::sa_mask成员可以人为指定。 在信号处理函数的执行过程中,需要加入进程信号掩码中的信号,并在信号处理函数执行完之后,自动解除对这些信号的屏蔽。
// 进程A
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigint1 (int signum)
{printf ("收到SIGINT信号!睡眠中...\n");sleep (3);printf ("睡醒了。\n");
}
void sigint2 (int signum, siginfo_t* si, void* pv)
{printf ("收到发自%u进程的SIGINT信号!\n", si -> si_pid);printf("附加的数据是:%d\n",si->si_value.sival_int);//printf("%s\n",si->si_value.sival_ptr);
}
int main ()
{printf("我是进程%u\n",getpid());struct sigaction act = {};act.sa_handler = sigint1;act.sa_sigaction = sigint2;act.sa_flags = SA_SIGINFO;
sigaction (SIGINT, &act, NULL);for (;;) pause ();
return 0;
}
// 进程B 给进程A发送附带数据的信号
#include <sys/wait.h>
int main (void)
{pid_t pid;printf("请输入进程号:");scanf("%u",&pid);
sigval_t sv; sv.sival_int = 6666;// sv.sival_ptr = "hehehe";
if (sigqueue (pid, SIGINT, sv) == -1) { perror ("sigqueue");return -1; } return 0;
}
七、定时器
系统为每个进程维护三个定时器:
-
真实计时器:进程运行的实际时间
-
虚拟计时器:进程运行在用户态所消耗的时间
-
实用计时器:进程运行在用户态和内核态消耗的时间之和
实际真正的时间(真实计时器) = 用户态时间 + 内核时间 + 睡眠时间 + 状态切换耗时
通过设置计时器的起始时间和重复间隔时间给进程设置定时事件,例如:定时保存、定时上传操作。
获取\设置定时器:
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
功能:获取当前进程的某个定时器的定时方案
int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
功能:设置当前进程的定时方案
which:指定哪个定时器,取值:ITIMER_REAL 真实计时器 SIGALRM(14)ITIMER_VIRTUAL 虚拟计时器 SIGVTALRM(26)ITIMER_PROF 实用计时器 SIGVTALRM(26)
struct itimerval {struct timeval it_interval; // 间隔时间,定时器开启后,每间隔这个时间就会再发出一次定时器信号,一直重复struct timeval it_value; // 开始时间,从该时间开始发送第一个定时器信号
};
// 时间结构体
struct timeval {time_t tv_sec; // 秒数suseconds_t tv_usec; // 微秒,不能超过1000000
};
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void sigalrm(int num)
{printf("闹钟响了!\n");
}
int main(int argc,const char* argv[])
{signal(SIGALRM,sigalrm);
// 准备定时方案struct itimerval timer = {{1,500000},{5,500000}};
// 设置定时器setitimer(ITIMER_REAL,&timer,NULL);
for(;;);
}
相关文章:
操作系统(信号处理)
一、信号介绍 什么是中断: 当进程接收到消息后中止当前正在执行的任务,转而执行其它任务,等待其它任务执行完毕后再返回继续执行。这种执行模式称为中断,分为硬件中断和软件中断两种 什么是信号: 信号是UNIX、类UNI…...
[MRCTF2020]Ezpop
[MRCTF2020]Ezpop 题目是pop,考的其实就是pop链,可以自己先学学,啥也不会QAQ php反序列化之pop链_pop3.phpwelcome-CSDN博客 POP 面向属性编程(Property-Oriented Programing) 常用于上层语言构造特定调用链的方法,与二进制利用…...
24暑假算法刷题 | Day27 | 贪心算法 I | LeetCode 455. 分发饼干,376. 摆动序列,53. 最大子数组和
目录 455. 分发饼干题目描述题解 376. 摆动序列题目描述题解 53. 最大子数组和题目描述题解 455. 分发饼干 点此跳转题目链接 题目描述 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i&#x…...
Golang 的空接口有什么用?
空接口在 Go 语言中具有多种重要用途: 实现通用的数据结构 例如,可以创建一个包含空接口类型元素的切片或映射,从而能够存储不同类型的值。这在处理多种未知类型的数据时非常有用。比如,一个日志系统可能会将不同类型的日志消息&a…...
计算机毕业设计选题推荐-课程教学平台-Java/Python项目实战
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...
健身日记之倒立俯卧撑学习——起始日2024.6.4
文章目录 目录 前言上期预期 昔日计划 新目标计划 瓶颈突破尝试 参考视频及文章 前言 两个月过去了,已经有所突破了,但是比较预期还是有较大差距,忘记更新csdn了,平时抖音视频号记录的多一些。 上期预期 2024.6.4开始尝试突…...
pikachu文件包含漏洞
一:漏洞基础 程序在引用文件的时,引用的文件名存在可控的情况,传入的文件名没有经过合理的校验或校验不严,从而操作了预想之外的文件,就有可能导致文件泄漏和恶意的代码注入; 文件包含漏洞概念 在PHP程序…...
09.FreeRTOS时间片调度与任务相关函数
文章目录 09. FreeRTOS时间片调度与任务相关函数1. FreeRTOS时间片调度2. 任务状态查询API函数3. 任务时间统计API函数 09. FreeRTOS时间片调度与任务相关函数 1. FreeRTOS时间片调度 时间片调度简介: 时间片调度实验流程: 核心代码: 开…...
git分支介绍
git branch 查看当前分支情况 可以看见当前只有一个分支叫main,也就是默认分支,可以理解为树的主干,git早期版本中默认分支叫master 命令行创建一个新分支 git branch [分支名]在创建之后,如果需要切换到新分支需要git switc…...
vm虚拟机下安装CentOS7系统
VMware16安装CentOS7 1.启动之前安装的VM 具体VMware安装过程 2.配置Linux(centos7)的镜像文件 选择安装镜像文件 4.开启虚拟机 开始读秒安装 选择安装过程中使用的语言,这里选择英文、键盘选择美式键盘。点击Continue 首先设置时间…...
python-报数(赛氪OJ)
[题目描述] 有 n 人围成一圈,顺序排号。 从第 1 个人开始报数(从 1 到 3 报数),凡是报到 3 的人退出圈子,问最后留下的是原来的第几号的那位。输入格式: 初始人数 n 。输出格式: 最后一人的初始…...
灵办AI:智能插件,办公与编程的得力助手
目录 引言一、灵办AI:智能化的办公伙伴二、编程能力:🔥代码阅读,学习助手🔥1、代码解读2、代码续写3、代码优化 三、插件端对话功能:智能交互,流畅体验四、翻译功能:一键翻译&#x…...
食家巷小程序:传统面点与平凉特产的美味盛宴
在美食的世界里,总有一些角落等待着我们去探索,而食家巷小程序就是这样一个为您开启美食宝藏的钥匙。 一、传统面点,传承千年的美味 食家巷小程序为您呈现了种类丰富的传统面点,每一款都蕴含着深厚的历史和文化底蕴。 平凉锅盔&…...
矢量文件坐标转换:2000坐标系转换为wgs84坐标系,具体代码实现
最近在处理矢量样本的时候,遇到一些shp文件的坐标系为2000坐标,需要统一地把非WGS84坐标系的矢量转换为WGS84坐标系。 本文记录一下如何进行2000坐标系转化为wgs84坐标系的过程。 在处理矢量数据转换的过程中,有几个关键步骤确保了数据的有效…...
MySQL-InnoDB引擎
目录 逻辑存储结构 架构 概述 内存结构 Buffer Pool(缓冲池) Change Buffer(更改缓冲区) Adaptive Hash Index(自适应hash索引) Log Buffer(日志缓冲区) 磁盘结构 System T…...
【Material-UI】复杂按钮 (Complex Button) 自定义详解
文章目录 一、ButtonBase 组件简介二、实例讲解:创建复杂的图片按钮1. 样式定义2. 核心组件构建3. 交互效果 三、高级自定义技巧1. 响应式设计2. 动态内容与动画 四、总结 在现代 Web 应用中,按钮不仅仅是一个点击交互元素,它们也承载着传递信…...
IT服务质量管理攻略(至简)
质量管理、风险管理和信息安全管理是IT服务监督管理的重要内容,三者之间相对独立。IT服务质量管理是通过制订质量方针、质量目标和质量计划,实施质量控制、质量保证和质量改进活动,确保IT服务满足服务级别协议的要求,最终获得用户…...
MySQL事务隔离级别、InnoDB使用MVCC+各种锁实现了RC和RR事务隔离级别、具体案例
事务隔离级别 脏读:一个事务读取到另一个未提交事务的更改。不可重复读:一个事务在两次读取同一数据时,发现数据被另一个已提交事务修改了。幻读:一个事务在读取过程中,因其他事务的插入而导致返回的行数不一致&#…...
你的Java项目还在等待吗?快来学会线程池,解放你的性能!
文章目录 你的Java项目还在等待吗?快来学会线程池,解放你的性能!1 什么是线程池?为什么需要它?2 线程池的参数有哪些?3 不同类型的线程池有哪些配置? 你的Java项目还在等待吗?快来学…...
深入解析:Amazon Bedrock 上 Claude 3 Haiku 的微调测试报告
前言 2024年7月10日,Anthropic Claude 3 Haiku 的微调功能在 Amazon Bedrock 上开放预览。本篇文章将分享 Claude 3 Haiku 的微调使用步骤及微调后模型的评估结果。 LLM 细调的优势 通过细调,LLM可以获得特定领域的知识或新知识。这样,与RA…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
