Featured image of post 嵌入式 Linux 系统编程之进程间通信 —— 信号机制深度剖析

嵌入式 Linux 系统编程之进程间通信 —— 信号机制深度剖析

嵌入式 Linux 系统编程之进程间通信 —— 信号机制深度剖析

信号机制的基础

信号的概念

信号是Unix、类Unix等系统里实现进程通信的一种方式。信号采用的是异步通信的机制。

当一个信号发送给一个某进程,操作系统就会中断接收到信号的进程,此时进程中任何非原子性的操作都将被中断。

同步通信:进程发出请求后阻塞等待响应。

异步通信:指进程发出请求后不等待相应,继续执行其他任务,等待响应时再处理。

比如去银行办事,异步像取号后可做别的事,叫号了再去办理;同步则是排队一直等。信号机制能提升程序执行效率。

信号的类型

Linux系统中提供了shell命令: kill - l,该命令的作用是给某个进程发送信号,参数-l可以列出信号的名称

image-20260205200622928

image-20260205200632317

可以发现Linux系统中的信号编号为164,其中编号为131的信号为普通信号,编号为34~64的信号为实时信号。

  1. 普通信号

    Linux系统中的普通信号也称为不可靠信号,当进程接收多个信号来不及处理,会丢弃多余信号,只保留一个。Linux系统的普通信号是Unix系统继承过来的。

  2. 实时信号

    Linux系统的实时信号也被称为可靠信号,当进程接收多个信号请求时但是不能及时处理,会把未处理的信号形成队列,按照排序依次处理。Linux系统中的实时信号是新增加的。

信号的含义

在Linux系统中可以查看man手册的第七章查找signal进行了解

image-20260205201544805

image-20260205201630013

image-20260205201633025

通过kill -l输出的信号名称都是大写的,按照编程规则而言这些信号都是宏定义,另外,可以看到信号前面有一个编号,这个编号就是指信号宏定义的替换列表的值。这些宏定义都被定义在signal.h头文件中

image-20260205201733253

信号的产生与处理

信号的产生

  1. **按键触发:**在终端,按Ctrl + C可产生SIGINT信号终止前台进程。这是因为按键产生硬件中断,操作系统解释为信号记录在进程 PCB,CPU 处理完中断后处理信号,使进程终止。

image-20260205201950731

image-20260205202020059

在终端正在运行的进程叫做前台进程,用户使用快捷键Ctrl+C只能结束前台进程,无法结束后台进程,如果想把一个进程转为后台执行,则可以在运行程序的时候添加符号 &

  1. **硬件异常:**进程执行除以0或者访问非法内存地址等操作引发硬件异常,内核会发出相关信号。

    image-20260205202337879

  2. 调用接口:kill()函数可以指定进程发信号,参数为目标进程的IP和信息,成功返回0,失败返回-1。raise()函数只能向当前进程发信号。

image-20260205202710204

image-20260205202815494

  1. **发送指令:**终端用kill命令发送信号,本质调用kill函数,不指定信号名,默认发SIGTERM信号终止进程。

image-20260205203904492

  1. 内核检测:内核检测到如闹钟超时(alarm()函数设置)产生SIGALRM信号,或进程向读端关闭的管道写数据产生SIGPIPE信号 。

image-20260205203954748

信号的处理

当进程接收到信号之后,可以分为三种情况来对信号进行处理,分别是默认、捕捉和忽略。

  1. **默认处理:**Linux系统对普通信号有默认规定,如果用户没有自定义信号的执行动作,则会采用默认处理的方式对信号进行响应。比如进程接收SIGTERM信号后则会被终止。

  2. **捕捉信号:**进程可提前设计信号相应函数并关联信号,这样当进程接收到信号之后,就不会执行信号的默认响应动作,而是执行用户指定的响应动作。signal函数用于此,参数为信号编号和处理函数的地址。

    image-20260205204656991

当捕捉到信号时,不论进程的主控制流程当前执行到哪儿,都会先跳到信号处理函数中执行,从信号处理函数返回后再继续执行主控制流程。

信号处理函数是一个单独的控制流程,因为它和主控制流程是异步的,二者不存在调用和被调用的关系,并且使用不同的堆栈空间。引入了信号处理函数使得一个进程具有多个控制流程,如果这些控制流程访问相同的全局资源(全局变量、硬件资源等),就有可能出现冲突。

SIGKILLSIGSTOP信号是不可自定义响应接口。

  1. 忽略信号:调用signal()函数,将第二个参数设为SIG_IGN可忽略信号 。

信号阻塞与挂起机制

信号阻塞

进程又是需要蚕食屏蔽某些信号,如程序执行时不想被Ctrl + C强制结束,可以阻塞该信号。

Linux系统中提供了一个名称叫做sigprocmask()的函数接口来设置信号集的属性,使用规则如下:

image-20260205205717136

image-20260205205857007

sigprocmask()函数用于设置信号集属性,实现阻塞、解除阻塞操作。需先创建信号集,添加或删除信号 。

阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

image-20260205210619244

信号挂起

进程只有在运行态才能处理信号,挂起信号集存储待处理信号。其他进程发的信号先放入挂起信号集,等进程被调度运行时再处理 。

补充知识

  1. 原子操作

原子操作(Atomic Operation) 指的是不可被中断、不可被分割的最小执行单元—— 要么整个操作完整执行完毕,要么完全不执行,不会出现 “执行到一半被打断” 的中间状态。

  1. 原子上下文

原子上下文(Atomic Context) 指的是执行流处于 “不可被调度、不可被阻塞” 的执行环境,在这个上下文中,所有操作都必须是原子的,且不能执行任何可能导致阻塞或调度的操作。

简单总结:原子操作是 “点” 上的原子性,原子上下文是 “段” 上的原子性—— 原子操作是原子上下文的基础,原子上下文要求所有操作都必须是原子的。

最后更新于 2026-03-26 23:00
...