6 Linux系统中进程通信之信号
本篇要点:
2025/12/30
①简要介绍信号是什么、如何产生信号以及信号的分类,在1.3.3有所有的信号的列表。
②介绍信号的三种处理方式,分别为默认处理 SIG_DFL、忽略信号SIG_IGN和捕获信号(自定义信号处理函数)。
③简短介绍信号从产生到处理完成的流程。
④详细说明信号的相关函数,发送信号的函数:kill、 raise 和 alarm,以及等待接收信号函数 pause,处理信号函数 signal。
⑤最后附有信号相关的3给综合示例。
目录
1.2.1 硬件方式产生的信号(Hardware-Generated Signals)
1.2.2 软件方式产生的信号(Software-Generated Signals)
1.3.1.1 不可靠信号(Non-Reliable Signals)
1.3.1.2 可靠信号(Reliable Signals)
1.3.2.1 非实时信号(Non-Real-Time Signals)
1.3.2.2 实时信号(Real-Time Signals)
1.5.1 阶段一:信号产生(Signal Generation)
1.5.2 阶段二:信号在进程中注册(Signal Registration)
1.5.3 阶段三:信号处理(Signal Handling)
1 信号的概述
1.1 信号的本质
在 Linux 系统 中,信号(Signal)本质上是一种软中断机制(Software Interrupt),用于通知进程发生了某种异步事件。
它是 软件层面对硬件中断机制的模拟,在概念上,一个进程接收到信号与处理器接收到硬件中断请求非常相似。
信号是 进程间通信(IPC)的一种异步通信机制:
-
进程在执行过程中无需主动等待信号的到来;
-
信号可能在任何时刻由内核或其他进程异步地发送过来;
-
被信号打断后,进程会根据预设的处理方式(默认处理、忽略、自定义函数)来应对该事件。
除了基本的事件通知功能外,信号机制还具备一定的控制与管理能力,例如:
-
可以控制进程的执行状态(终止、暂停、恢复等);
-
可以实现进程间的事件同步与异常响应。
1.2 信号的来源
信号的产生途径多种多样,根据产生条件的不同,可以大体分为 两类:硬件方式 和 软件方式。
1.2.1 硬件方式产生的信号(Hardware-Generated Signals)
硬件事件在运行过程中触发信号,通常由 内核检测到异常或外部输入事件 所引起。常见情况包括:
1.2.1.1 键盘操作触发
用户在终端输入特定组合键时,终端驱动程序会向前台进程发送信号。
例如:
|
操作键 |
信号名称 |
信号编号 |
默认动作 |
是否可捕获 |
作用对象 |
默认处理行为 |
说明 |
|
Ctrl + C |
SIGINT |
2 |
终止进程 |
✅ 可捕获 |
前台进程组中的所有进程 |
终止进程 |
常用的中断信号,常用于终止正在运行的程序 |
|
Ctrl + Z |
SIGTSTP |
20 |
暂停进程 |
✅ 可捕获 |
前台进程组中的所有进程 |
挂起进程(暂停),可用 fg 或 bg 恢复 |
用于暂停进程,之后可恢复 |
|
Ctrl + \ |
SIGQUIT |
3 |
终止并生成 core |
✅ 可捕获 |
前台进程组中的所有进程 |
终止进程并生成 core 文件 |
常用于调试,程序异常退出时产生 core dump |
|
Ctrl + D |
EOF(非信号) |
— |
输入结束 |
❌ |
输入流 |
关闭标准输入流 |
表示输入流结束,并非信号 |
1.2.1.2 程序运行错误触发
-
当程序在运行中出现异常或非法操作时,内核会自动向该进程发送信号:
-
空指针访问、非法内存访问 → SIGSEGV(段错误)
int *p = NULL; *p = 12456; // 触发段错误,进程被内核终止 -
除以 0 → SIGFPE(算术错误信号)
-
执行非法指令 → SIGILL
-
1.2.2 软件方式产生的信号(Software-Generated Signals)
软件层面可以通过系统调用或命令来产生信号,这些信号通常由进程主动触发或系统函数自动触发。
1.2.2.1 命令触发
-
通过命令行工具向进程发送信号,例如:
kill -9 1234 # 向 PID 为 1234 的进程发送 SIGKILL 信号(内部实际上是调用了
kill()系统调用。)
1.2.2.2 函数触发
-
程序中可以通过以下函数主动发送信号:
kill(pid_t pid, int sig); // 向指定进程发送信号 raise(int sig); // 向当前进程发送信号 sigqueue(pid_t pid, int sig, const union sigval value); // 带参数发送信号 -
示例:
kill(getpid(), SIGINT); // 向自己发送中断信号
1.3 信号的种类
Linux 系统中的信号可以从不同角度进行分类,主要包括 按可靠性分类 和 按时间特性分类。
1.3.1 按可靠性分类
1.3.1.1 不可靠信号(Non-Reliable Signals)
-
早期 UNIX 系统 设计较为简单,信号机制存在缺陷:
当同一种信号在尚未处理完时再次到达,后续信号可能被丢失。 -
因此,这类可能丢失的信号被称为 “不可靠信号”。
-
在 Linux 中,信号编号 1~31 的传统信号属于不可靠信号。
特点:
-
不支持信号排队(同类型信号可能被覆盖)
-
信号值固定,语义预定义(例如 SIGINT、SIGKILL 等)
1.3.1.2 可靠信号(Reliable Signals)
-
为解决信号丢失问题,Linux 在原有信号机制基础上扩展了一组新的信号,称为 可靠信号。
-
这类信号支持 信号排队机制:
当一个信号正在被处理时,如果再次收到同类信号,会进入信号队列中,等待前一个信号处理完成后依次处理。 -
因此,不会发生信号丢失的情况。
范围:
-
可靠信号编号从
SIGRTMIN~SIGRTMAX(一般为 34~64)。
特点:
-
支持排队(不会丢失)
-
可附带参数(通过
sigqueue()发送) -
信号含义可由用户自定义
1.3.2 按时间特性分类
1.3.2.1 非实时信号(Non-Real-Time Signals)
-
非实时信号即早期的 不可靠信号。
-
每个信号都有固定含义,系统预定义行为,例如:
-
SIGINT:中断进程 -
SIGKILL:强制结束进程
-
-
不支持排队机制,如果在信号处理期间收到相同信号,后者会被忽略。
总结:
非实时信号的特点是:
“固定含义,不支持排队,可能丢失”
1.3.2.2 实时信号(Real-Time Signals)
-
实时信号是 POSIX 标准 新增的信号类型,属于可靠信号的一种。
-
允许用户自定义信号编号及其处理逻辑。
-
具有 排队 和 优先级 特性:
-
多个相同信号可以依次排队执行;
-
编号越小的实时信号优先级越高;
-
可以使用
sigqueue()附带数据发送。
-
总结:
实时信号的特点是:
“可自定义,支持排队,不丢失,具备优先级”
1.3.3 信号列表

| 信号名 | 数值 | 英文名称 | 默认行为 | 说明 |
|---|---|---|---|---|
| SIGHUP | 1 | Hang Up | 终止进程 | 控制终端挂起或控制进程终止(终端关闭) |
| SIGINT | 2 | Interrupt | 终止进程 | 键盘中断(Ctrl + C) |
| SIGQUIT | 3 | Quit | 终止并产生 core dump | 键盘退出(Ctrl + \) |
| SIGILL | 4 | Illegal Instruction | 终止并产生 core dump | 非法指令(执行了错误的机器码) |
| SIGTRAP | 5 | Trace Trap | 终止并产生 core dump | 调试断点或陷阱 |
| SIGABRT | 6 | Abort | 终止并产生 core dump | 调用 abort() 函数触发 |
| SIGBUS | 7 | Bus Error | 终止并产生 core dump | 总线错误(内存访问未对齐) |
| SIGFPE | 8 | Floating Point Exception | 终止并产生 core dump | 浮点异常(除以零等) |
| SIGKILL | 9 | Kill | 立即终止 | 强制杀死进程,不能被捕获或忽略(此信号不可更改处理方式) |
| SIGUSR1 | 10 | User-defined Signal 1 | 终止进程 | 用户自定义信号1 |
| SIGSEGV | 11 | Segmentation Violation | 终止并产生 core dump | 非法内存访问(段错误) |
| SIGUSR2 | 12 | User-defined Signal 2 | 终止进程 | 用户自定义信号2 |
| SIGPIPE | 13 | Broken Pipe | 终止进程 | 向无读端的管道写数据 |
| SIGALRM | 14 | Alarm Clock | 终止进程 | 定时器信号(来自 alarm()) |
| SIGTERM | 15 | Terminate | 终止进程 | 请求进程正常终止(可捕获) |
| SIGSTKFLT | 16 | Stack Fault | 终止进程 | 协处理器栈错误(很少使用) |
| SIGCHLD | 17 | Child Status Changed | 忽略 | 子进程结束或状态改变时发送给父进程 |
| SIGCONT | 18 | Continue | 继续运行 | 恢复被暂停的进程(不能被阻塞) |
| SIGSTOP | 19 | Stop | 暂停进程 | 暂停进程(不可捕获或忽略) |
| SIGTSTP | 20 | Terminal Stop | 暂停进程 | 键盘暂停(Ctrl + Z) |
| SIGTTIN | 21 | Background Read | 暂停进程 | 后台进程尝试从终端读输入 |
| SIGTTOU | 22 | Background Write | 暂停进程 | 后台进程尝试往终端写输出 |
| SIGURG | 23 | Urgent Condition | 忽略 | 套接字紧急(out-of-band)数据到达 |
| SIGXCPU | 24 | CPU Time Limit Exceeded | 终止并产生 core dump | 超出 CPU 时间限制 |
| SIGXFSZ | 25 | File Size Limit Exceeded | 终止并产生 core dump | 超出文件大小限制 |
| SIGVTALRM | 26 | Virtual Alarm Clock | 终止进程 | 虚拟时间定时器信号 |
| SIGPROF | 27 | Profiling Timer Expired | 终止进程 | 统计定时器信号 |
| SIGWINCH | 28 | Window Change | 忽略 | 终端窗口大小改变 |
| SIGIO | 29 | I/O Possible | 终止进程 | 异步 I/O 事件(可读可写) |
| SIGPWR | 30 | Power Failure | 终止进程 | 电源故障(UPS事件) |
| SIGSYS | 31 | Bad System Call | 终止并产生 core dump | 错误的系统调用 |
| SIGRTMIN ~ SIGRTMAX | 34~64 | Real-Time Signals | 默认终止 | 实时信号(可自定义,用于线程或实时通信) |
1.4 信号的处理方式
在 Linux 系统中,每个信号都可以被进程以不同方式处理,主要分为 三类:
1.4.1 默认处理(SIG_DFL)
-
含义:使用系统为该信号预定义的默认动作。
-
特点:
-
对大多数信号,默认动作是 终止进程;
-
对少数信号,默认动作可能是 忽略信号 或 停止进程。
-
-
示例:
#include <signal.h>
#include <stdio.h>
int main() {
signal(SIGINT, SIG_DFL); // Ctrl+C 将按默认方式终止进程
while(1); // 无限循环等待信号
return 0;
}
1.4.2 忽略信号(SIG_IGN)
-
含义:进程 忽略该信号,收到信号时不做任何处理,相当于屏蔽信号。
-
特点:
-
对于无法忽略的信号(如
SIGKILL,SIGSTOP),此方式无效; -
常用于忽略临时无关紧要的信号。
-
-
示例:
#include <signal.h>
#include <stdio.h>
int main() {
signal(SIGINT, SIG_IGN); // 忽略 Ctrl+C 信号
while(1); // 无限循环,按 Ctrl+C 不会终止程序
return 0;
}
1.4.3 捕获信号(自定义处理函数)
-
含义:进程为信号注册一个 自定义处理函数(类似中断服务函数),信号到来时执行该函数。
-
特点:
-
可以执行用户定义的逻辑,例如打印消息、清理资源、计数等;
-
捕获信号的函数原型:
void handler(int signum);
-
-
示例:
#include <signal.h>
#include <stdio.h>
void sig_handler(int signum) {
printf("收到信号 %d\n", signum);
}
int main() {
signal(SIGINT, sig_handler); // 注册自定义处理函数
while(1); // 无限循环,按 Ctrl+C 时不会终止
return 0;
}
1.5 信号通信的流程
一个完整的信号生命周期可以分为 三个阶段,涉及 四个关键事件:
1.5.1 阶段一:信号产生(Signal Generation)
-
发生位置:内核(Kernel)
-
触发方式:
-
硬件触发:如空指针访问、非法指令、键盘组合键(Ctrl+C)
-
软件触发:如
kill()、raise()、sigqueue()
-
-
说明:
内核负责生成信号,并将其标记到目标进程的信号位图中,等待进程处理。
1.5.2 阶段二:信号在进程中注册(Signal Registration)
-
发生位置:用户进程(User Process)
-
操作:
-
进程通过
signal()或sigaction()注册信号处理函数 -
也可以选择忽略信号 (
SIG_IGN) 或使用默认动作 (SIG_DFL)
-
-
说明:
-
用户进程不能直接给其他用户进程发送信号,必须通过 内核
-
用户空间只负责告诉内核如何处理信号
-
1.5.3 阶段三:信号处理(Signal Handling)
-
发生位置:用户进程
-
操作:
-
当进程在用户态运行时,内核检查是否有待处理信号
-
若有,则暂停当前执行,调用注册的处理函数执行信号响应逻辑
-
信号处理完成后,恢复原来的程序执行
-
1.5.4 信号通信补充说明
-
用户进程不能直接向其他用户进程发送信号
-
必须通过内核转发
-
例子:
进程A --> 内核 --> 发送信号 --> 进程B
-
-
用户空间不具备发送信号的能力
-
进程只能将信号请求提交给内核,由内核完成实际发送
-
2 信号相关函数
2.1 发送信号
2.1.1 kill() —— 向进程发送信号
① 头文件
#include <sys/types.h> //pid_t
#include <signal.h>
② 函数原型
int kill(pid_t pid, int sig);
③ 函数参数
| 函数参数 | 参数类型 | 参数功能 |
|---|---|---|
pid |
pid_t |
指定要发送信号的目标进程或进程组: • • • • |
sig |
int |
要发送的信号编号,例如: • • • • |
④ 返回值
| 返回值 | 返回值类型 | 返回值功能 |
|---|---|---|
| -1 | int |
失败时返回 -1,并设置 errno 以指示错误原因。 |
| 0 | int |
成功时返回 0。 |
常见错误号:
-
ESRCH:指定的进程不存在; -
EPERM:没有权限向该进程发送信号。
⑤ 函数功能
kill() 函数用于向指定的进程发送信号。这个信号可以用来通知进程进行特定操作,比如终止进程(SIGTERM)或强制杀死进程(SIGKILL)。即使发送的信号是 0,kill() 也会检查进程是否存在,而不发送实际的信号。
-
如果
pid为正数,kill()向指定的进程发送信号。 -
如果
pid为0,kill()向当前进程组的所有进程发送信号。 -
如果
pid为-1,则发送信号给所有进程,除了进程1(init)。
如果目标进程拒绝某个信号(如 SIGKILL),则 kill() 会失败。
kill() 函数与 kill 命令的区别
| 对比项 | kill() 函数 |
kill 命令 |
|---|---|---|
| 使用位置 | C 语言程序中调用 | 在终端命令行中使用 |
| 参数顺序 | kill(pid, sig) → 先进程,后信号 |
kill -sig pid → 先信号,后进程 |
| 示例 | kill(1234, 9); |
kill -9 1234 |
⑥ 示例代码
利用 kill() 函数结束进程
思路
-
第一个终端运行目标程序。
-
第二个终端运行本程序,通过命令行参数发送信号结束目标进程。
kill.c
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
int main(int argc,char *argv[]) {
if(argc != 3) {
printf("用法:%s -sig pid", argv[0]);
printf("示例:%s -9 1234",argv[0]);
return -1;
}
int sig = atoi(argv[1]) * (-1);
pid_t pid = (pid_t)atoi(argv[2]);
int ret = kill(pid, sig);
if(ret == -1) {
printf("用法:%s -sig pid", argv[0]);
printf("示例:%s -9 1234",argv[0]);
perror("kill failed");
return -1;
}
else {
printf("成功%s pid为%d 的进程", argv[0], pid);
}
return 0;
}
count.c
#include <unistd.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
printf("PID:%d\n", getpid());
int count = 1;
while (count++) {
printf("count:%d\n", count);
sleep(1);
}
return 0;
}

2.1.2 raise() —— 发送信号给当前进程
① 头文件
#include <signal.h>
② 函数原型
int raise(int sig);
③ 函数参数
| 函数参数 | 参数类型 | 参数功能 | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sig | int |
要发送的信号,通常是信号常量,表示将该信号发送给调用本函数的进程本身。 常用信号:
|
④ 返回值
| 返回值 | 返回值类型 | 返回值功能 |
|---|---|---|
| 0 | int |
成功时返回 0。 |
| -1 | int |
失败时返回 -1,并设置 errno 以指示错误原因。 |
⑤ 函数功能
raise() 函数用于向当前进程发送指定的信号。它等效于调用 kill(getpid(), sig),即向当前进程发送信号 sig。
-
常见的信号包括:
SIGINT(中断信号,通常由 Ctrl+C 触发)、SIGTERM(终止信号)、SIGSEGV(段错误信号)等。 -
raise()是一个同步函数,发送信号后会根据信号类型触发相应的处理动作,如信号处理函数的调用或者进程终止。
raise() 适用于向当前进程发送信号,用来触发进程的信号处理行为。
⑥ 示例代码
#include <stdio.h>
#include <signal.h>
void handler(int sig) {
printf("捕获到信号 %d\n", sig);
}
int main(void) {
// 注册信号处理函数
signal(SIGINT, handler);
printf("程序开始运行...\n");
// 发送信号给自己
raise(SIGINT);
printf("程序正常结束。\n");
return 0;
}

2.1.3 alarm() —— 设置定时器
① 头文件
#include <unistd.h>
② 函数原型
unsigned int alarm(unsigned int seconds);
③ 函数参数
| 函数参数 | 参数类型 | 参数功能 |
|---|---|---|
| seconds | unsigned int |
定时器倒计时的秒数,表示多少秒后发送 SIGALRM 信号给当前进程。如果设置为 0,则取消定时器。 |
④ 返回值
| 返回值 | 返回值类型 | 返回值功能 |
|---|---|---|
| 0 | unsigned int |
如果定时器之前没有设置,返回 0。 |
| x | unsigned int |
如果定时器之前已设置,返回之前设置的秒数,即剩余的时间。 |
⑤ 函数功能
alarm() 函数用于设置一个定时器,经过指定的时间(以秒为单位)后,向当前进程发送 SIGALRM 信号。SIGALRM 信号可以被捕获并通过相应的信号处理函数进行处理。alarm() 返回值是之前设置的定时器的剩余时间(如果有的话)。
-
如果定时器已经设置并且还有剩余时间,
alarm()会返回剩余的秒数。 -
如果设置为
0,则会取消定时器,并且不会发送SIGALRM信号。
常见用法是设置超时机制,或者定时执行某些操作。
⑥ 示例代码
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int sig) {
printf("收到信号 %d:定时器时间到!\n", sig);
}
int main(void) {
// 注册 SIGALRM 信号处理函数
signal(SIGALRM, sig_handler);
printf("设置定时器 5 秒...\n");
alarm(5); // 5秒后产生SIGALRM信号
// 主进程持续运行,等待信号
for (int i = 1; i <= 10; i++) {
printf("第 %d 秒\n", i);
sleep(1);
}
return 0;
}

2.2 等待接收信号
2.2.1 pause() —— 暂停进程直到接收到信号
① 头文件
#include <unistd.h>
② 函数原型
int pause(void);
③ 函数参数
pause() 不接受任何参数。
④ 返回值
| 返回值 | 返回值类型 | 返回值功能 |
|---|---|---|
| -1 | int |
如果调用失败,返回 -1,并设置 errno。常见的错误是 EINTR(由于信号中断)。 |
| 无返回值 | 无 |
pause() 会一直阻塞直到接收到信号,因此如果成功调用时,它没有返回值,除非因信号中断而返回。 |
⑤ 函数功能
pause() 函数会使当前进程挂起,直到接收到一个信号为止。信号会被送到进程并触发相应的信号处理函数。pause() 会阻塞当前进程,直到信号处理函数被调用,或者信号导致进程终止。
-
pause()是一个阻塞调用,它会让进程停止执行,直到收到信号。 -
如果信号处理函数返回时,
pause()会返回-1,并且errno会设置为EINTR,表示调用被信号中断。
pause() 常用于在信号驱动的程序中,保持进程等待信号的到来,通常与信号处理程序配合使用。
⑥ 示例代码
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int sig) {
printf("收到信号 %d:唤醒进程!\n", sig);
}
int main(void) {
// 注册信号处理函数
signal(SIGALRM, sig_handler);
printf("设置定时器为 5 秒...\n");
alarm(5); // 5秒后触发SIGALRM信号
printf("进程进入休眠,等待信号...\n");
pause(); // 暂停执行,直到收到信号
printf("pause() 返回,程序继续运行。\n");
return 0;
}

2.3 处理信号
2.3.1 signal() —— 设置信号处理函数
① 头文件
#include <signal.h>
② 函数原型
typedef void (*__sighandler_t)(int);
__sighandler_t signal(int __sig, __sighandler_t __handler);
等价于
void (*signal(int __sig, void (*__handler)(int)))(int);
虽然原型比较复杂,但可以理解为:
signal(信号编号, 信号处理函数);
这个函数的难点在返回值和第二个参数,其实返回值和第二个参数的类型是一样的,都是函数指针__sighandler_t,也就是说这个函数的第二参数的参数类型是函数指针,返回值也是函数指针。
这个函数容易会被看成一个函数指针,但实际上它是指针函数,只是它的返回值特殊一点,是函数指针。

关于函数指针和指针函数的区别,可以参考C与指针。
③ 函数参数
| 函数参数 | 参数类型 | 参数功能 | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
__sig |
int |
要捕获的信号编号,例如 SIGINT、SIGTERM、SIGALRM 等。 |
||||||||
__handler |
void (*)(int) |
信号处理函数指针,用于处理捕获的信号。该函数应接受一个
|
⚠️ 信号处理函数声明格式:
void sigX_handler(int arg);
当捕获到信号时,系统会自动调用此函数,并将信号编号作为参数传入。
④ 返回值
| 返回值 | 返回值类型 | 返回值功能 |
|---|---|---|
| 旧处理函数 | __sighandler_t |
返回之前指定的信号处理函数指针。如果设置失败,返回 SIG_ERR。 |
⑤ 函数功能
signal() 函数用于为指定的信号设置一个信号处理函数,该函数会在信号发生时被调用。该函数提供了一个简单的机制来捕获并处理特定的信号。信号处理函数是由程序员提供的,用于定义接收到信号时应该执行的行为。
-
signal()的第一个参数是信号编号,指定要捕获的信号。 -
第二个参数是信号处理函数的指针。当该信号发生时,操作系统会暂停当前进程的执行,并调用该信号处理函数。
-
处理信号的函数通常会接受一个整数类型的参数,该参数是信号的编号。
signal() 的返回值是先前设置的信号处理函数,可以通过返回的函数指针来恢复或替换信号处理程序。如果设置信号处理程序失败,signal() 返回 SIG_ERR。
常见信号处理方法:
-
SIG_IGN:忽略信号。 -
SIG_DFL:恢复默认行为(如进程终止、核心转储等)。
-
自定义函数 → 调用该处理函数。
⑥ 示例代码
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
int count = 0;
void signal_handler(int sig) {
count++;
printf("\ncount :%d\n", count);
fflush(stdout);
}
int main() {
signal(SIGINT, signal_handler);
while (1) {
printf("程序运行中\n");
printf("count = 5 时程序退出\n");
sleep(1);
if(count == 5) {
printf("count = 5 程序退出\n");
exit(2);
}
}
}
程序运行过程:
-
程序启动,进入无限循环。
-
每秒打印
"程序运行中"和"count = 5 时程序退出"。 -
每次收到
Ctrl + C(SIGINT信号)时,count会增加并打印出当前值。 -
一旦
count达到 5,程序会打印"count = 5 程序退出",然后退出。

3 综合示例
3.1 综合案例1
0~5s发送系统信号2 忽略 5~10秒发送信号2 执行处理函数的内容 10秒后执行信号默认功能
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void SIGINT_Handler(int sig) {
printf("捕获到信号 %d SIGINT\n", sig);
}
int main() {
int second = 1;
//0 ~ 5s 忽略 SIGINT 信号
signal(SIGINT, SIG_IGN);
printf("0~5s 忽略系统信号2 (ctrl+c)\n");
while(second <= 5) {
printf("%d s\n", second++);
sleep(1);
}
//5 ~ 10s 发送信号2 执行处理函数的内容
signal(SIGINT, SIGINT_Handler);
while(second > 5 && second <= 10) {
printf("%d s\n", second++);
sleep(1);
}
//10 ~ 15s 执行系统默认的功能
signal(SIGINT, SIG_DFL);
while(second > 10) {
printf("%d s\n", second++);
sleep(1);
}
return 0;
}

3.2 综合案例2
可以调用对应的信号处理函数,可以触发多个信号,例如:2信号 3信号 4信号 调用同一个信号处理函数。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
// 通用信号处理函数
void signal_handler(int signo)
{
switch (signo)
{
case SIGINT:
printf("捕获到信号 2 (SIGINT):键盘中断信号 Ctrl+C\n");
break;
case SIGQUIT:
printf("捕获到信号 3 (SIGQUIT):键盘退出信号 Ctrl+\\\n");
break;
case SIGILL:
printf("捕获到信号 4 (SIGILL):非法指令信号\n");
break;
default:
printf("捕获到其他信号:%d\n", signo);
break;
}
}
int main(void)
{
printf("综合案例2:多个信号共用同一个处理函数\n");
printf("请尝试按 Ctrl+C、Ctrl+\\ 或使用 kill 命令发送信号\n");
// 注册信号(2、3、4 都调用同一个处理函数)
signal(SIGINT, signal_handler); // 信号 2
signal(SIGQUIT, signal_handler); // 信号 3
signal(SIGILL, signal_handler); // 信号 4
// 主循环
while (1)
{
printf("程序正在运行中... PID = %d\n", getpid());
sleep(3);
}
return 0;
}
触发信号的方式
| 信号编号 | 信号名称 | 触发方式 |
|---|---|---|
| 2 | SIGINT |
终端按 Ctrl+C |
| 3 | SIGQUIT |
终端按 Ctrl+\ |
| 4 | SIGILL |
用 kill -4 <pid> 命令发送(模拟非法指令) |
现象:

3.3 综合案例3
每隔5秒钟,输出一下当前的时间。
思路:需要使用定时器alarm + signal对应的信号处理函数
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
// 通用信号处理函数
void signal_handler(int signo)
{
// 每次触发时输出系统时间
system("date");
// 再次启动定时器,实现周期触发
alarm(5);
}
int main(void)
{
printf("程序PID:%d\n", getpid());
int i=1;
// 注册SIGALRM信号
signal(SIGALRM, signal_handler);
// 启动第一次定时
alarm(5);
// 主进程持续运行,等待信号触发
while (1)
{
printf("%ds\r\n",i++);
sleep(1);
}
return 0;
}
现象:

更多推荐


所有评论(0)