
【Linux】信号机制详解:进程间通信的核心
- 摘要
- 目录
-
- [1. 信号机制概述](#1. 信号机制概述)
-
- [1.1 信号的本质](#1.1 信号的本质)
- [1.2 信号的来源](#1.2 信号的来源)
- [2. 信号的分类与常见信号](#2. 信号的分类与常见信号)
-
- [2.1 标准信号(1-31号)](#2.1 标准信号(1-31号))
- [2.2 实时信号(34-64号)](#2.2 实时信号(34-64号))
- [3. 信号的生命周期](#3. 信号的生命周期)
-
- [3.1 信号的产生(Generation)](#3.1 信号的产生(Generation))
- [3.2 信号的递达(Delivery)](#3.2 信号的递达(Delivery))
- [3.3 信号的阻塞(Blocking)](#3.3 信号的阻塞(Blocking))
- [4. 信号处理方式](#4. 信号处理方式)
-
- [4.1 执行默认动作](#4.1 执行默认动作)
- [4.2 忽略信号](#4.2 忽略信号)
- [4.3 捕获信号(自定义处理函数)](#4.3 捕获信号(自定义处理函数))
- [5. 信号编程实践](#5. 信号编程实践)
-
- [5.1 使用signal()函数(简单方式)](#5.1 使用signal()函数(简单方式))
- [5.2 使用sigaction()函数(推荐方式)](#5.2 使用sigaction()函数(推荐方式))
- [5.3 发送信号](#5.3 发送信号)
- [6. 可靠信号与不可靠信号](#6. 可靠信号与不可靠信号)
-
- [6.1 不可靠信号的问题](#6.1 不可靠信号的问题)
- [6.2 可靠信号的优势](#6.2 可靠信号的优势)
- [7. 信号在实际开发中的应用](#7. 信号在实际开发中的应用)
-
- [7.1 优雅退出](#7.1 优雅退出)
- [7.2 子进程管理](#7.2 子进程管理)
- [7.3 定时任务](#7.3 定时任务)
- 总结
- 相关链接
摘要
信号(Signal)是Linux系统中最古老、最基础的进程间通信(IPC)机制之一。它为进程提供了一种异步通知机制,允许操作系统或其他进程向目标进程发送特定事件的通知。本文将深入探讨Linux信号机制的原理、分类、处理方式以及实际应用,帮助读者全面掌握这一核心技术。
相关标签: #Linux #信号机制 #进程间通信 #系统编程 #IPC
目录
1. 信号机制概述
信号是Linux内核提供的一种软件中断机制 ,用于通知进程某个特定事件已经发生。与管道、消息队列等IPC机制不同,信号是异步的------进程无需等待信号到达,可以继续执行其他任务,当信号到达时才会被中断处理。
1.1 信号的本质
信号可以理解为进程级别的"中断",它打断进程的正常执行流程,转而执行预定义的信号处理函数。处理完成后,进程可以选择恢复执行或终止。
1.2 信号的来源
信号可以来自多个来源:
- 硬件异常:如除零错误、非法内存访问(SIGSEGV)
- 用户操作:如按下Ctrl+C(SIGINT)、Ctrl+Z(SIGTSTP)
- 系统调用 :如
kill()、raise()、alarm()等 - 内核事件:如子进程终止(SIGCHLD)、管道破裂(SIGPIPE)
2. 信号的分类与常见信号
Linux系统定义了多达64种信号,可分为两大类:
2.1 标准信号(1-31号)
这些是传统UNIX信号,也称为不可靠信号。常见信号包括:
| 信号名 | 编号 | 默认动作 | 含义 |
|---|---|---|---|
| SIGHUP | 1 | 终止 | 终端挂起或控制进程终止 |
| SIGINT | 2 | 终止 | 键盘中断(Ctrl+C) |
| SIGQUIT | 3 | 终止+core | 键盘退出(Ctrl+\) |
| SIGKILL | 9 | 终止 | 强制终止(不可捕获) |
| SIGSEGV | 11 | 终止+core | 段错误(非法内存访问) |
| SIGTERM | 15 | 终止 | 终止信号(可捕获) |
| SIGCHLD | 17 | 忽略 | 子进程状态改变 |
| SIGSTOP | 19 | 停止 | 停止进程(不可捕获) |
2.2 实时信号(34-64号)
实时信号是POSIX标准引入的可靠信号 ,支持排队、携带数据,且按发送顺序递送。信号范围为SIGRTMIN到SIGRTMAX。
3. 信号的生命周期
信号从产生到处理经历以下阶段:
3.1 信号的产生(Generation)
当事件发生时,内核为目标进程生成信号,并将该信号添加到进程的未决信号集(Pending Signal Set)中。
3.2 信号的递达(Delivery)
当进程从内核态返回用户态时,内核检查未决信号集。如果存在未被阻塞的信号,则执行相应的处理动作。
3.3 信号的阻塞(Blocking)
进程可以通过信号屏蔽字(Signal Mask)阻塞某些信号。被阻塞的信号会保持未决状态,直到解除阻塞。
注意 :SIGKILL和SIGSTOP这两个信号无法被阻塞、忽略或捕获。
4. 信号处理方式
进程对信号的处理有三种方式:
4.1 执行默认动作
每个信号都有默认处理动作,主要包括:
- Term:终止进程
- Ign:忽略信号
- Core:终止进程并生成core dump文件
- Stop:停止进程
- Cont:继续执行已停止的进程
4.2 忽略信号
使用signal()或sigaction()将信号处理设置为SIG_IGN:
c
signal(SIGINT, SIG_IGN); // 忽略Ctrl+C
4.3 捕获信号(自定义处理函数)
注册自定义信号处理函数:
c
void sig_handler(int signo) {
printf("Received signal %d\n", signo);
}
signal(SIGINT, sig_handler);
5. 信号编程实践
5.1 使用signal()函数(简单方式)
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int sig) {
printf("捕获到信号 %d,但我不会退出!😎\n", sig);
}
int main() {
signal(SIGINT, handler); // 注册SIGINT处理函数
printf("进程运行中,按Ctrl+C测试...\n");
while(1) {
sleep(1);
}
return 0;
}
5.2 使用sigaction()函数(推荐方式)
sigaction()是更可靠的信号处理接口,提供更多控制选项:
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
void handler(int sig, siginfo_t *info, void *context) {
printf("信号 %d 来自进程 %d\n", sig, info->si_pid);
}
int main() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO; // 使用sa_sigaction而非sa_handler
sigemptyset(&sa.sa_mask); // 清空信号屏蔽字
sigaction(SIGUSR1, &sa, NULL);
printf("进程PID: %d,等待SIGUSR1信号...\n", getpid());
while(1) pause(); // 挂起进程,等待信号
return 0;
}
5.3 发送信号
c
#include <signal.h>
#include <sys/types.h>
// 向指定进程发送信号
kill(pid, SIGUSR1);
// 向自己发送信号
raise(SIGUSR1);
// 设置定时器信号
alarm(5); // 5秒后发送SIGALRM
6. 可靠信号与不可靠信号
6.1 不可靠信号的问题
早期UNIX的标准信号(1-31)存在以下问题:
- 信号丢失:相同信号多次到达时,只记录一次
- 竞态条件:信号处理函数执行期间,可能再次收到同一信号
- 信号处理函数重入:可能导致数据不一致
6.2 可靠信号的优势
实时信号(SIGRTMIN-SIGRTMAX)解决了这些问题:
- 支持排队:多个相同信号不会丢失
- 携带数据 :可通过
sigqueue()传递额外信息 - 有序递送:按照发送顺序处理
c
union sigval value;
value.sival_int = 100;
sigqueue(pid, SIGRTMIN, value); // 发送实时信号并携带数据
7. 信号在实际开发中的应用
7.1 优雅退出
捕获SIGTERM信号实现资源清理:
c
volatile sig_atomic_t keep_running = 1;
void sig_handler(int sig) {
keep_running = 0; // 设置退出标志
}
int main() {
signal(SIGTERM, sig_handler);
while(keep_running) {
// 业务逻辑
}
// 清理资源
printf("正在清理资源...\n");
return 0;
}
7.2 子进程管理
处理SIGCHLD信号回收子进程,避免僵尸进程:
c
void sigchld_handler(int sig) {
while(waitpid(-1, NULL, WNOHANG) > 0); // 非阻塞回收
}
signal(SIGCHLD, sigchld_handler);
7.3 定时任务
使用SIGALRM实现定时功能:
c
void alarm_handler(int sig) {
printf("定时器触发!⏰\n");
alarm(10); // 重新设置10秒定时器
}
signal(SIGALRM, alarm_handler);
alarm(10); // 首次设置
总结
信号机制是Linux进程间通信的重要组成部分,虽然它传递的信息量有限,但其异步特性使其在系统编程中不可或缺。掌握信号机制需要理解以下要点:
✅ 信号的本质 :软件层面的异步通知机制
✅ 信号分类 :标准信号与实时信号的区别
✅ 处理方式 :默认、忽略、捕获三种模式
✅ 可靠性问题 :使用sigaction()代替signal()
✅ 实际应用:优雅退出、子进程管理、定时任务等场景
在实际开发中,应优先使用sigaction()函数和实时信号,避免不可靠信号带来的竞态条件和信号丢失问题。同时,信号处理函数应保持简洁,避免调用非异步信号安全的函数。
相关链接
- Linux Signal手册页(man 7 signal)
- POSIX信号处理规范
- Linux进程间通信(IPC)机制详解
- 信号安全函数列表(async-signal-safe)
- Linux系统编程实战教程
✨ 坚持用 清晰易懂的图解 + 代码语言, 让每个知识点都 简单直观 !
🚀 个人主页 :不呆头 · CSDN
🌱 代码仓库 :不呆头 · Gitee
📌 专栏系列 :
💬 座右铭 : "不患无位,患所以立。"
