Linux进程信号详解(一):信号快速认识

一、信号快速认识

  • 信号(现实生活中):闹钟、红绿灯、上课铃声、狼烟、电话铃声、肚子叫、敲门声、脸色不好 ....

1.1 生活中的信号 ------ 快递的例子

想象你网购了很多商品:

  • 你能识别快递:你知道快递员打电话时该怎么处理。即使快递没有到来,你也知道快递来临的时候,你该如何处理快递 。 ------ 这是内置的识别能力

  • 信号产生前你就知道处理方法: 即使快递没到,你也知道"收到快递要拆开" ------ 进程在信号产生前就已经准备好了处理函数

  • **不必立即处理:**你正在打游戏,可以5分钟后下楼取快递 ------信号不要求立即处理,而是在"合适的时候"处理。

  • 记住有信号:你知道有一个快递到了但还没取 ------ 进程会保存未决信号

  • 处理方式有三种

    • 默认动作:开心拆快递

    • 自定义动作:(买了零食,分给同学一些)

    • 忽略:不理快递

基本结论

  • 识别信号是内核程序员内置的特性。

  • 信号产生前,进程已经知道如何处理。

  • 信号处理不是立即的,而是在合适的时候(从内核态返回用户态时)。

  • 信号到来 → 信号保存 → 信号处理。

  • 信号处理方式:忽略 ; 默认 ; 自定义【后续都叫信号捕捉】

快递到来的整个过程,对你来说,是异步的, 你不确定 , 快递员什么时候会给你打电话

1.2 键盘产生的信号

Makefile

复制代码
testSign:testSign.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f testSign

testSig.cc

复制代码
#include <iostream>
#include <unistd.h>

int main()
{
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world," << cnt++ << std::endl;
        sleep(1);
    }

    return 0;
}

ctrl + c , 是给目标进程发送信号的 。 相当一部分的信号处理动作 , 就是让进程自己终止!

1.3 信号都有哪些?

kill -l :查看信号列表数字)大写的信号名

关键说明

  1. 每个信号都有编号宏定义名称 (如 2 号信号对应 SIGINT),宏定义在signal.h头文件中;
  2. 34 及以上为实时信号,用于实时进程通信,日常开发主要使用 34 以下的常规信号;
  3. 每个信号都有默认处理动作,可通过man 7 signal查看详细说明(如 SIGKILL 的默认动作是终止进程,且无法被捕捉和忽略)。

常用信号:

编号 名称 默认动作 含义
2 SIGINT 终止 终端中断(Ctrl+C)
3 SIGQUIT 终止+Core Dump 终端退出(Ctrl+\)
9 SIGKILL 终止 强制终止(不可捕捉、不可阻塞)
11 SIGSEGV 终止+Core Dump 段错误(非法内存访问)
14 SIGALRM 终止 闹钟信号
20 SIGTSTP 停止进程 终端停止(Ctrl+Z)

ctrl + c : 给目标进程发送 2)信号 !!!

收到信号,处理动作(捕捉):

1.4 证明:ctrl+c -> 二号信号

修改一下testSig.cc的代码

复制代码
#include <iostream>
#include <unistd.h>
#include <signal.h>

void handlerSig(int sig)
{
    std::cout << "获得了一个信号: " << sig << std::endl;
}

int main()
{
    signal(SIGINT,handlerSig);
    int cnt = 0;
    while (true)
    {
        std::cout << "hello world, " << cnt++ << std::endl;
        sleep(1);
    }

    return 0;
}

二、前台 & 后台进程?

2.1 现象查看

2.2 核心定义

特性 前台进程 (Foreground) 后台进程 (Background)
标准输入 ✅ 独占键盘输入 ❌ 无法从 stdin 读取
标准输出 ✅ 可以输出到终端 ✅ 可以输出到终端
键盘信号 ✅ 响应 Ctrl+C 等 ❌ 不响应键盘信号
数量限制 必须只有一个 可以有多个
创建方式 ./program ./program &
  • 前台进程唯一能接收键盘输入、获取标准输入的进程,同一时间 Shell 中只能有一个前台进程
  • 后台进程 :无法从键盘获取标准输入的进程,同一时间可以有多个后台进程
  • Shell 命令行进程本身:默认是前台进程,负责接收用户输入的命令。

2.3 为什么前台进程只能有一个?

键盘只有一个,输入数据一定是给一个确定的进程的!

这是硬件层面的限制------键盘是独占设备 。如果多个进程同时等待键盘输入,操作系统无法决定把按键发给谁。因此 Shell 设计了**作业控制(Job Control)**机制:

  • 前台作业:当前与终端交互的进程组

  • 后台作业:在后台运行,不占用终端输入

2.4 进程状态转换与作业控制命令

命令 作用 示例
jobs 查看所有后台任务 [1]+ Running ./test &
fg %n 将后台任务 n 提到前台 fg %1
bg %n 让暂停的后台任务继续运行 bg %1
Ctrl+Z 暂停当前前台进程,放入后台 进程变为 Stopped
Ctrl+C 向前台进程发送 SIGINT 信号 终止进程

2.5 关键场景:孤儿进程与后台化

复制代码
# 场景1:正常前台进程
$ ./testsig          # bash 子进程作为前台进程
# Ctrl+C 可以终止,因为键盘信号发给前台进程

# 场景2:后台运行
$ ./testsig &        # 立即放入后台
# Ctrl+C 杀不掉!因为键盘信号只发给前台进程

父进程先退出 → 孤儿进程自动后台化

复制代码
// 代码逻辑示意
int main() {
    if (fork() == 0) {
        // 子进程
        sleep(10);  // 模拟工作
    } else {
        // 父进程先退出
        exit(0);
    }
}

现象

  • 父进程(bash 的子进程)先退出

  • 子进程变成孤儿进程,被 init/systemd 收养

  • 自动提到后台(因为失去了控制终端的前台关联)

  • Ctrl+C 杀不掉 (已经是后台进程)

三、给进程发送信号的理解

发送信号的本质:修改目标进程的内核数据结构 (向目标进程写信号 = 修改位图)

信号是一种异步事件通知机制

  • 信号产生后,不是立即处理

  • 进程必须先把信号记录下来

  • 合适的时候(系统调用返回、中断返回)统一处理

为什么必须是操作系统来发送?

信号在内核中的存储:位图结构

复制代码
// include/linux/sched.h
struct task_struct {
    // ...
    unsigned long signal;      // 共享挂起信号(线程组共享)
    unsigned long blocked;     // 被阻塞的信号掩码
    struct sigpending pending; // 私有挂起信号队列
    // ...
};

位图解析

四、信号VS通信IPC

对比维度 信号(Signal) 通信 IPC(管道/消息队列/共享内存等)
本质 通知机制 数据传输机制
传递内容 只传递"发生了什么"(事件类型) 传递"具体的内容"(任意数据)
存储方式 用位图记录(1 bit) 用缓冲区存储(多字节)
数据承载 不能带数据(除 siginfo 外) 可以传输任意数据
同步方式 异步、立即记录 可能阻塞、支持同步/异步
控制权 操作系统强制干预 进程间协作完成
类比 门铃(有人来了) 快递(有具体物品)
相关推荐
thinkMoreAndDoMore1 小时前
linux内核匹配I2C设备
linux·运维·服务器
灵感__idea5 小时前
Hello 算法:“走一步看一步”的智慧
前端·javascript·算法
小政同学7 小时前
【NFS故障】共享的文件无法执行
linux·运维·服务器
AI木马人7 小时前
3.【Prompt工程实战】如何设计一个可复用的Prompt系统?(避免每次手写提示词)
linux·服务器·人工智能·深度学习·prompt
lwf0061647 小时前
导数学习日记
学习·算法·机器学习
ch3nyuyu7 小时前
Ubuntu(乌班图)基础指令
linux·运维·网络
minglie18 小时前
gcc编译器汇总
linux
头发够用的程序员8 小时前
从滑动窗口到矩阵运算:img2col算法基本原理
人工智能·算法·yolo·性能优化·矩阵·边缘计算·jetson
武帝为此8 小时前
【数据清洗缺失值处理】
python·算法·数学建模
Halo_tjn9 小时前
Java 基于字符串相关知识点
java·开发语言·算法