(Linux)信号与中断
信号是操作系统发给进程的通知用于处理突发事件如用户中断或程序错误控制进程行为。信号的产生不影响进程的运行二者是异步的。信号的产生与处理基本概念给进程产生信号的信号源非常多主要有四种一是键盘在进程运行时按下Ctrl C终止进程本质上是键盘向目标进程发送了信号而进程面对大部分信号的处理方式就是让自己终止二是异常在程序运行出错时由操作系统内核自动产生如除0或段错误与CPU有关在后面讲解三是软件条件这个比较复杂在文章末尾会进行讲解四是其它进程通过调用系统调用向指定进程发送信号例如在代码中调用系统调用接口kill、raise等常用的kill命令底层调用的就是kill接口。使用命令 kill -l 可以查看全部62个信号及其编号注意没有32和33号。其中34号到64号为实时信号。一旦接收到实时信号进程必须立即处理而前面的1到31号的普通信号则不需要立即处理。我们在进程运行时按下Ctrl C就是发送2号信号。若没有特别说明下面说的信号主要指普通信号。进程在接收到信号后 不一定立即处理而是在合适的时候进行处理处理方式分三种一是默认处理动作即终止进程二是自定义信号处理动作三是忽略处理即不处理。进程对信号的处理方式是提前设定好的在信号没有产生时进程就知道该如何处理了。默认处理动作默认处理动作可以在手册中查看使用命令 man 7 signal 并向下翻找可以看到各个信号以及对应的说明这里是用字母排序。第三列的Action即是进程的默认处理方式Term表示终止进程Ign表示忽略信号Stop表示暂停Cont表示继续运行。Core表示核心转储然后终止执行该操作会在当前路径下形成一个文件里面是进程异常退出时在内存中的核心数据这就是核心转储Core Dump。但在云服务器上为了防止大量的文件占据空间这个功能默认是被禁止的。不过可以使用命令 ulimit -c [文件大小] 暂时开启。文件大小设为0表示关闭该功能。当我们实在找不到代码中哪里出错时可以开启core dump再让代码运行崩溃此时会生成核心转储文件一般命名为core然后使用gdb调试输入core-file [核心转储文件名]这样会帮我门直接定位到出错行这就是事后调试。在wait函数和waitpid函数 详见Linux进程控制-CSDN博客从参数status返回的退出信息中core dump标志就是表示是否core dump终止信号部分就是位图标记了进程接收到的信号。自定义信号处理动作信号的处理动作可以在代码中使用接口signal更改也就是自定义处理动作。signal的第一个参数signum是信号编号用于指定修改哪个信号的处理动作。第二个参数handler是一个函数指针定义在下图中间那行。我们实现好处理信号的函数后将其指针传入即可。完成之后进程在接收到相同的信号时就会调用handler指向的函数。要注意的是9号信号是无法修改的这是为了防止恶意程序无法强制终止。信号保存基本概念实际执行信号的处理动作称为信号递达Delivery信号从产生到递达之间的状态称为信号未决pending,进程接收到信号后除了执行递达的动作还可以阻塞信号。被阻塞的信号会保持在未决状态直到进程解除对其的阻塞才会执行递达的动作。阻塞与忽略并不相同只要信号被阻塞就不会被处理而忽略是进程对信号的一种处理动作。比起阻塞更贴切的说法是“屏蔽”。因为是否阻塞信号并不是在收到信号后才决定的在信号产生之前进程就已经确定哪些信号要阻塞了。由于接收到信号后进程并不一定会立即处理所以进程会把信号保存在task_struct中。其中与信号相关的成员变量主要是这三张表block、pending和handler。Linux提供的信号操作都是通过这三张表实现的。接收到的信号就保存在pending中它的类型为unsigned int。这个成员是当作位图使用的第k位上为1就表示接收到了k号信号。普通信号在处理前无论接收到了几次都只算一次所以使用位图记录即可。发送信号的本质就是修改进程task_struct中的位图由于task_struct位于操作系统内核只有操作系统有权修改因此发送信号只能通过操作系统。block也是位图它的第k位表示是否阻塞k号信号值为1即表示阻塞为0则不阻塞。简单来说就是个“黑名单”里面标为1的都是被屏蔽的信号。默认情况下进程不会屏蔽任何信号。handler是函数指针数组它下标为k的位置上存储了k号信号的处理方法前面的signal函数就是通过修改它来修改信号处理方法。从上图来看每个信号只有⼀个bit的未决标志非0即1 不记录该信号产生了多少次阻塞标志也是这样表示的。因此 未决和阻塞标志可以用统一的数据类型sigset_t来存储sigset_t称为信号集这个类型的变量也是当作位图使用每一个bit可以表示是否被标记了对应信号在阻塞信号集中是否被标记的含义是该信号是否被阻塞而在未决信号集中是否被标记的含义是该信号是否处于未决状态。相关接口即将要介绍的几个接口都离不开信号集。而对信号集的操作有一套专门的接口如下#include signal.h int sigemptyset(sigset_t *set);//将set所指向的信号集的所有bit全部置为0即清空 int sigfillset(sigset_t *set);//与上面相反这个接口会将所有bit全部置1 int sigaddset(sigset_t *set, int signo);//将对应bit置为1即添加对应信号 int sigdelset(sigset_t *set, int signo);//将对应bit置为0即除去对应信号 int sigismember(const sigset_t *set, int signo);//判断是否包含对应信号下面介绍相关接口位图block可以借助函数sigprocmask进行操作第二个参数set是信号集sigprocmask会根据其中的内容对block进行修改具体如何修改与第一个参数how有关how是选项用于确定进行什么操作有如下三种选择how选择第一个选项时sigprocmask会向block添加set中标记的信号选择第二个选项则是去掉set中标记的信号如果要将整个block换成set就使用第三个选项。最后一个参数oldset是输出型参数sigprocmask会将修改前的block传出可以用来回退修改。函数sigpending则用于获取pendingset是输出型参数pending的拷贝通过set传出。接口sigaction用于读取和修改与指定信号相关的处理动作第一个参数signum是信号编号第二个参数用于选择如何处理信号稍后讲解oldact是输出型参数会把修改前的处理动作传出用于回退修改。结构体sigaction的定义如下struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); };我们只看第一个成员sa_handler和第三个成员 sa_mask。之前我们提到使用signal接口可以修改信号处理动作如果我们定义一个sigaction结构体变量将实现好的信号处理动作赋值给成员sa_handler其它成员不用处理将结构体的指针传入参数act也可以实现相同的效果。sa_mask的使用比较复杂它也是信号集。当某个信号的处理函数被调用时内核会自动暂时屏蔽该信号当信号处理函数返回时再恢复这样就保证了在处理某个信号时如果这种信号再次产生那么它会被阻塞到当前处理结束为止。防止同一个信号重复递达。如果在调用信号处理函数时除了当前信号被自动屏蔽之外还希望自动屏蔽另外⼀些信号则用sa_mask标记这些需要额外屏蔽的信号当信号处理函数返回时自动恢复原来的信号屏蔽字。信号捕捉信号捕捉的流程如下图所示内核态和用户态我们后面再讲解现在暂时以这种简单的方式进行理解要执行用户编写的程序时CPU就会进入用户态要执行操作系统内核的程序时CPU就会进入内核态。只要是处于内核态转向用户态的临界点就会对信号进行捕捉。在进程运行时会经常因为中断、异常等原因被暂停让操作系统先处理完。下图就展示了每次进程被暂停时的流程在这个过程中包含了两次CPU从内核态到用户态的转换因此会进行两次信号捕捉。比较特殊的是进行到下图第3步时如果该信号没有自定义处理动作那么就会直接在内核态下执行默认处理动作亦或者进程没有收到信号操作系统处理完中断或异常后CPU切回用户态执行之前暂停的代码这样就会少了一次从内核态转到用户态的过程。其次如果同时捕捉了多个信号通常会按照信号编号顺序依次执行信号处理动作。如果在执行自定义信号处理方法的过程中因为调用系统调用或收到中断等又转为内核态再转回用户态则会再次捕捉信号如果真的捕捉到了没有被屏蔽的信号则会暂停当前的信号处理动作执行新信号的处理动作完成后再返回继续执行。要注意的是在执行信号处理动作时会暂时屏蔽同种信号执行完了才会恢复。硬件中断由外部设备触发的中断系统运行流程叫做硬件中断。以键盘为例当进程执行到scanf函数时会进入阻塞状态等待。那么当键盘按下回车完成输入时进程是怎么知道的呢事实上除了通过内存所有的外部设备还通过中断控制器与CPU间接相连只是不会从这里直接发送数据到CPU。以键盘为例键盘输入完成准备就绪后会向中断控制器发送中断请求中断控制器会通过针脚识别出是哪个设备发送的并将其编号转换为中断号然后通知CPUCPU接收到后会立即暂停当前正在执行的任务将寄存器中的数据存入栈区在内存。接着CPU会从中断控制器中获取中断号。而操作系统维护了一张中断向量表IDT这张表可以简单地看成函数指针数组里面是各个中断处理的方法通过中断号CPU就能在表中找到并运行对应的中断处理方法从而读取键盘数据调用中断处理方法实际上就是运行操作系统的程序。完成后CPU会继续执行之前的任务。总的来看就是键盘准备就绪后发送硬件中断最终使操作系统读取键盘数据。操作系统并不会周期性地检测外部设备或管理资源而是一直处于暂停状态使用接口pause就能做到收到中断就运行一次然后继续暂停不断循环。但并不是没有外部设备准备就绪操作系统就不运行了。有一个硬件名为时钟源它一般集成在CPU内部会以固定的频率发送特定的中断——时钟中断接收到这个中断后CPU同样会通过中断向量表运行中断方法从软件层面上看就是让操作系统执行一系列操作其中就包括了进程调度详见Linux进程-CSDN博客。因此操作系统是在硬件时钟中断的驱动下进行进程调度的。实际上操作系统就是基于中断进行工作的软件而中断触发的频率就是CPU的主频。我们知道操作系统会根据每个进程的时间片分配CPU资源。在进程的task_struct中有一个计数器叫时间片每次操作系统收到时钟源的中断时就会让当前正被调度的进程的时间片减1当时间片为0时操作系统就会执行进程调度将该进程重新放回调度队列转而调度其它进程。CPU本身也会发送中断当CPU运行进程的代码指令出现除0、指针重复释放等错误时CPU也会生成中断号此时操作系统执行的中断服务就是给进程发送信号。软件中断前面提到操作系统是基于中断进行工作的我们平时使用的系统调用接口也是借助中断实现的。与硬件中断不同的是这种中断是由程序主动触发的被称为软件中断或简称软中断。虽然我们一般将系统调用当作函数使用但系统调用的过程与函数调用有很大的不同。所有系统调用接口都维护在一张系统调用表中它本质就是个函数指针数组。因此每一个系统调用都唯一对应一个下标这个下标叫做系统调用号。当程序调用系统调用会将操作系统提供的系统调用号放入eax寄存器参数放入其它寄存器。接着执行软件中断指令int 0x80触发软件中断调用中断向量表中对应的中断处理函数——系统调用处理程序这个程序其实就是操作系统会根据eax寄存器中的系统调用号在系统调用表中找到对应的函数并执行。可以看出操作系统并没有直接提供系统调用的方法我们调用的open、close等接口其实是C语言封装的在其内部会通过宏定义将函数名转换成系统调用号正如前面所说这个系统调用号是操作系统提供的。当然软件中断指令也是操作系统提供的。现代CPU有更高效的syscall/svc指令但整体上逻辑是相同的只是不再需要传统的中断向量表。CPU内部因指令执行引发的硬中断如除0、缺页等被称为异常。而CPU内部的软中断如int 0x80或syscall被称为陷阱。不难发现操作系统提供的资源管理、硬件管理和系统调用等所有的操作和服务都是通过中断触发的。用户态与内核态回顾进程地址空间的内容在地址空间的3G到4G的部分属于内核空间。与用户空间不同的是这一部分是通过内核页表进行虚拟地址到物理地址的转换的。且由于操作系统内核只有一个所以也只需要一个内核页表。而用户空间使用的用户页表因为要保证进程间的独立性所以存在多个。将内核空间映射到进程的地址空间使得CPU收到中断后能直接通过进程的地址空间找到操作系统进而调用中断服务。可以明确的是用户即我们写的程序是不能访问内核空间的。CPU有两种状态内核态和用户态只有处于内核态才能访问内核空间否则进程会被CPU直接终止。在CPU的寄存器cs中就记录了CPU处于哪种状态0表示内核态3表示用户态。相关内容补充前台进程与后台进程进程有前台进程和后台进程两种键盘产生的信号只能发给前台进程。当我们使用 ./[文件名] 直接运行程序时默认会在前台运行可以从标准输入中获取内容即获取键盘输入的内容这种进程被称为前台进程。相应的使用 ./[文件名] 可以创建后台进程它在后台运行无法从标准输入中获取内容。前台进程只能有一个这是因为键盘只有一个只能向一个进程中输入数据。而后台进程则可以有多个。解析命令的bash就是前台进程当我们输入命令后创建前台进程后bash就会被操作系统换到后台。另外当前台进程运行时按下Ctrl Z会暂停前台进程此时该进程会被操作系统换到后台将bash换回前台否则用户就无法操作了。使用命令jobs可以查看当前的后台进程方框中的数字即为任务号使用命令 fg [任务号] 即可将对应的后台进程换至前台。相应的使用命令 bg [任务号] 可以将对应的前台进程换至后台。软件条件、定时器在操作系统中信号的软件条件指的是由软件内部状态或特定软件操作触发的信号产生机制。这些条件包括但不限于定时器超时如alarm函数设定的时间到达、软件异常如向已关闭的管道写数据产生的SIGPIPE信号等。alarm就是一种定时器在代码使用alarm可以定时给自己发送SIGALRM信号。它的返回值是之前设定的定时器的剩余时间例如在代码中调用了两次alarm第一次定时10秒3秒后又调用了一次alarm定时为15秒那么之前的定时器剩下时间是7秒则这里会返回7。但是7秒后内核不会发送信号因为在第二次调用alarm时被重置为15秒后发送了。如果之前没有设置定时器则会返回0。由于操作系统内也会有很多定时器所以操作系统也需要将定时器管理起来管理的方式仍旧是用先描述再组织。它在内核中的数据结构如下。struct timer_list { struct list_head entry; unsigned long expires; void (*function)(unsigned long); unsigned long data; struct tvec_t_base_s *base; };其中值得注意的是expires和functionexpires存储的是 定时器剩余时间1000低于1000即为超时操作系统每次会从所有定时器中选出超时最久的定时器然后通过上面的函数指针function执行对应的功能。volatile关键字我们看下面这段死循环代码如果要终止循环只有在进程收到2号信号然后执行handler将全局变量flag改为1但在main函数中不会对flag进行修改在编译器优化级别较高的情况下可能会将全局变量flag直接优化到寄存器中每次访问时直接访问寄存器中的值。而进程收到了2号信号的处理方法只会修改位于在内存中的flag从而使得程序收到2号信号后没有像意料中那样退出。#include stdio.h #include signal.h int flag 0; void handler(int sig) { printf(chage flag 0 to 1\n); flag 1; } int main() { signal(2, handler); while(!flag); printf(process quit normal\n); return 0; }为此可以在定义变量flag时加上volatile关键字如此一来CPU每次访问flag的值时都会从内存中调取。volatile int flag 0;

相关新闻

无人机红外图像目标检测实战:YOLO轻量化模型与Django部署全流程

无人机红外图像目标检测实战:YOLO轻量化模型与Django部署全流程

最近在做一个比较有意思的项目,就是基于无人机视角的红外图像目标检测。项目是给某实验室的模拟场景做的技术预研,核心需求很简单:无人机挂载的红外相机拍到的画面,要能实时检测出地面上的特定目标(比如车辆、人员等&a…

2026/10/16 2:15:50 阅读更多 →
App Store 分析数据自动化:asc analytics-reports 如何采集、下载并校验私有报表

App Store 分析数据自动化:asc analytics-reports 如何采集、下载并校验私有报表

【免费下载链接】app-store-connect-cli-skills Skills to automate app store deployed and everything related to it using the asc cli 项目地址: https://gitcode.com/gh_mirrors/ap/app-store-connect-cli-skills 点击查看 免费下载 还在 App Store Connect …

2026/10/16 2:14:50 阅读更多 →
vscode与git

vscode与git

记录一下在vscode中clone代码与上传到仓库,其中几点小步骤 推荐观看 (推荐并不是说最好的,是我个人觉得有用的) dy视频: 1.https://www.douyin.com/video/7491109504080219418?count10&cursor0&enter_met…

2026/10/17 6:43:17 阅读更多 →

最新新闻

端侧智能推理优化指南:从量化到NPU的工程实践

端侧智能推理优化指南:从量化到NPU的工程实践

手里有几个端侧智能项目的推理优化落地之后,我终于有底气把这块硬骨头拆开讲讲。所谓“端侧智能推理优化与加速技术”,说白了就是让模型在手机、平板、车载盒子、边缘工控机这些资源有限的设备上,跑得又快又稳又省电。模型压缩、量化、算子替…

2026/10/17 11:04:15 阅读更多 →
端侧智能推理优化与加速:从模型压缩到运行时调度实战

端侧智能推理优化与加速:从模型压缩到运行时调度实战

端侧智能推理优化与加速这件事,我做了几年,最大的感受是:很多模型在服务器上跑得飞快,一挪到手机、开发板上就“翻车”——要么首帧延迟高得离谱,要么功耗撑不过半小时,要么精度掉到没法用。所谓“端侧高效…

2026/10/17 11:04:15 阅读更多 →
书匠策AI辅助毕业论文写作全流程实战指南

书匠策AI辅助毕业论文写作全流程实战指南

又是一年毕业季,我的私信列表里陆陆续续躺着几十条求助消息,内容几乎一个模子刻出来的:导师催着交开题报告、文献综述憋了一个月还是干巴巴、论文改了第七稿仍然被批逻辑断裂、查重报告飘红一片不知从何下手。毕业论文这四个字,确…

2026/10/17 11:04:15 阅读更多 →
Arduino摩尔斯电码库:零基础5分钟实现LED嘀嗒通信

Arduino摩尔斯电码库:零基础5分钟实现LED嘀嗒通信

简介:这是一份专为Arduino初学者与电子爱好者设计的摩尔斯电码功能扩展库,解决在嵌入式项目中快速实现字符→摩尔斯电码转换及LED信号输出的实际需求,适用于教学实验、创意交互装置开发与通信原理入门实践。资源包共4个文件,含核心…

2026/10/17 11:04:15 阅读更多 →
C++ 手写 AI 编程 Agent(3):工具注册表与文件操作工具集

C++ 手写 AI 编程 Agent(3):工具注册表与文件操作工具集

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 11:04:15 阅读更多 →
uniapp中scroll-view与swiper联动tab切换的实战避坑指南

uniapp中scroll-view与swiper联动tab切换的实战避坑指南

1. 项目概述:为什么这个组合在实际开发中总让人“手抖”做uniapp项目时,只要页面里有带tab栏的滑动切换需求,几乎没人能绕开scroll-view和swiper这两个组件。但凡你试过用它们搭一个“左右滑动切页顶部tab高亮联动”的功能,大概率…

2026/10/17 11:03:15 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →