前言:欢迎各位光临本博客,这里小编带你直接手撕**,文章并不复杂,愿诸君**耐其心性,忘却杂尘,道有所长!!!!

在这里插入图片描述


IF’Maxue个人主页

 🔥 个人专栏:
《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》

⛺️生活是默默的坚持,毅力是永久的享受。不破不立!

Linux进程信号详解

信号与信号量的区别

信号和信号量,就像“老婆”和“老婆饼”——没有任何关系。

信号的概念

什么是信号?

信号是一种给进程发送的异步事件通知机制。简单说,就是能“打断”进程正在做的事,让进程去处理这个通知。

比如生活中,你正在上课(进程运行),快递员敲门(信号产生),你需要暂停上课去处理快递(进程处理信号)。这里的关键是:快递员啥时候来不确定(信号异步产生),但你知道该怎么接快递(进程提前知道信号处理方式)。

image.png

基本结论

  1. 进程在信号产生前,就已经知道该怎么处理(提前“学会”了)。
  2. 信号不是立即处理,而是等进程到“合适的时机”再处理。
  3. 进程对信号的识别和处理方式,是操作系统设计时就确定好的。
  4. 信号的来源很多(键盘、系统调用、异常等)。

信号的产生方式

1. 键盘产生(仅前台进程)

键盘操作能给前台进程发信号,比如ctrl+c

  • kill -l可查看所有信号,1-31是普通信号(进程择时处理),34-64是实时信号(立即处理)。
    image.png
  • ctrl+c实际发送的是2号信号(SIGINT),默认处理动作是终止进程。
    image.png
信号的处理方式

进程处理信号有3种方式:

  • 默认动作(比如大部分信号默认终止进程);
  • 自定义动作(用代码修改处理逻辑);
  • 忽略(收到信号不做任何处理)。

image.png

用signal函数自定义信号处理

signal函数可以修改信号的默认处理动作,格式如下:
image.png

示例代码:
image.png

运行结果:ctrl+c不再终止进程,而是打印提示,因为我们把2号信号的处理动作改成了自定义函数。
image.png

前台与后台进程
  • 前台进程:直接运行(./xxx),能接收键盘信号(如ctrl+c)。
  • 后台进程:加&运行(./xxx &),不接收键盘信号,需用kill命令终止。
  • jobs查看后台任务,fg 1将后台任务1转为前台,ctrl+z将前台进程转后台,bg 1让后台进程继续运行。

image.png
image.png

2. 系统调用产生信号

发送信号的本质是修改进程内核数据结构(task_struct)中的信号位图(记录收到的信号)。操作系统提供了多个系统调用用于发送信号。

kill函数

给指定进程发信号,格式:
image.png

示例:给PID为1234的进程发9号信号(强制终止):kill(1234, 9)

image.png

raise函数

进程给自己发信号,示例代码:
image.png

运行结果:进程给自己发9号信号后终止(9号信号无法被自定义处理)。
image.png

abort函数

让进程自己终止,固定发送6号信号(SIGABORT),且会忽略自定义处理(强制用默认动作)。

image.png
运行结果:进程收到6号信号后终止。
image.png

3. 异常产生信号

程序运行出错时,操作系统会给进程发信号,导致进程崩溃。

除0错误

代码:
image.png

结果:触发8号信号(SIGFPE,浮点数错误),进程终止。
image.png

野指针错误

代码:
image.png

结果:触发11号信号(SIGSEGV,段错误),进程终止。
image.png

为什么操作系统会发信号?

操作系统是软硬件管理者,能检测CPU寄存器状态(如除0时的溢出标志)、内存访问错误(如野指针访问无效地址),一旦检测到,就给对应进程发信号。

4. 软件条件产生信号

某些软件场景会触发信号,比如管道读写错误、闹钟超时等。

alarm函数(闹钟)

给进程设置闹钟,时间到后操作系统发送14号信号(SIGALRM)。

  • 格式:unsigned int alarm(unsigned int seconds);
  • 功能:alarm(5)表示5秒后发SIGALRM;alarm(0)取消闹钟。
  • 返回值:上一个闹钟剩余的秒数(无则返回0)。

image.png

示例1:自定义处理SIGALRM
代码:
image.png

结果:5秒后收到14号信号,执行自定义打印。
image.png

示例2:重复闹钟
代码:在信号处理函数中重新设置闹钟,实现每秒触发一次。
image.png

结果:每秒打印一次提示。
image.png

pause函数

让进程暂停,直到收到一个信号才继续运行。

代码:结合alarm和pause,实现定时任务。
image.png

结果:每1秒执行一次任务。
image.png

操作系统如何管理闹钟?

操作系统用“先描述再组织”的方式管理多个闹钟:

  • 每个闹钟用结构体描述(包含目标进程、超时时间等)。
    image.png
  • 用类似“最小堆”的结构组织,堆顶是最早超时的闹钟,定期检查,超时则给进程发SIGALRM。

image.png

信号的保存

进程收到信号后不会立即处理,而是先保存起来。保存信号依赖三张“表”:pending(未决信号集)、block(阻塞信号集)、handler(处理函数集)。

image.png

三张表的作用

  • pending(未决信号集):位图,记录进程收到了哪些信号(比特位1表示收到,0表示未收到)。
  • block(阻塞信号集):位图,记录哪些信号被“屏蔽”(比特位1表示阻塞,此时即使pending有该信号,也不会处理)。
  • handler(处理函数集):函数数组,记录每个信号的处理方式(默认、忽略、自定义函数)。

image.png
image.png

信号集操作函数

Linux提供函数操作这三张表,核心是sigset_t(信号集类型,类似位图)。

image.png
image.png

常用函数
  • sigemptyset:初始化信号集为空(所有比特位0)。
  • sigfillset:初始化信号集为满(所有比特位1)。
  • sigaddset:向信号集添加某个信号(置位)。
  • sigdelset:从信号集删除某个信号(清位)。
  • sigismember:判断信号是否在信号集中。

image.png

sigprocmask:修改block表

用于设置进程的阻塞信号集(block表)。
image.png

sigpending:获取pending表

获取当前未决的信号集(pending表)。
image.png

整合示例

代码:阻塞2号信号,发送信号后查看pending表,再解除阻塞观察处理结果。

image.png
image.png

结果:

  • 阻塞期间,ctrl+c(2号信号)被记录在pending表(显示有未决信号)。
  • 解除阻塞后,信号被处理(执行自定义函数),pending表中对应位清0。

image.png

信号的处理

信号处理的时机:进程从内核态返回用户态时,会检查pending表中未被阻塞的信号,按handler表的方式处理。

处理流程:

  1. 检查pending表,找到未被block的信号。
  2. 清空该信号在pending表中的位(1→0)。
  3. 执行handler表中对应的处理动作(默认、忽略、自定义函数)。

image.png

补充知识

信号的重复产生

普通信号(1-31)多次产生时,pending表中只会记录一次(位图位只能是1);实时信号(34-64)会按顺序处理多次。

image.png

Core与Term的区别

进程终止的两种默认动作:

  • Term:直接终止进程,不保留现场。
  • Core:终止进程并生成core文件(核心转储),保存进程崩溃时的内存状态,用于调试。

image.png

开启Core dump

默认情况下,很多系统关闭了Core dump(避免占用空间),可通过ulimit临时开启:

  • ulimit -a:查看当前限制(core file size为0表示关闭)。
    image.png
  • ulimit -c unlimited:开启Core dump(不限制大小)。
使用core文件调试

进程崩溃生成core文件后,可用gdb调试:
gdb ./程序名 core,直接定位到出错行。

image.png

进程终止总结

image.png
image.png
image.png

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐