linux系统中进程通信之信号
目录
一、信号的概述
信号的本质
信号的来源
一、硬件方式产生的信号(Hardware-Generated Signals)
二、软件方式产生的信号(Software-Generated Signals)
信号的种类,以及处理流程
一、按可靠性分类
1️⃣ 不可靠信号(Non-Reliable Signals)
2️⃣ 可靠信号(Reliable Signals)
二、按时间特性分类
1️⃣ 非实时信号(Non-Real-Time Signals)
2️⃣ 实时信号(Real-Time Signals)
信号列表
键盘产生的信号
一、常见的键盘控制信号
二、信号说明
三、总结对比
信号的处理方式
1️⃣ 默认处理(SIG_DFL)
2️⃣ 忽略信号(SIG_IGN)
3️⃣ 捕获信号(自定义处理函数)
信号通信的流程
阶段一:信号产生(Signal Generation)
阶段二:信号在进程中注册(Signal Registration)
阶段三:信号处理(Signal Handling)
信号通信补充说明
二、发送信号
kill() ------ 给任一进程/进程组发送任一信号
1.头文件
2.函数原型
3.参数说明
4.返回值
kill() 函数与 kill 命令的区别
5.示例代码
raise() ------ 给当前线程发送任一信号
1.头文件
2.函数原型
3.函数参数
4.返回值
5.功能与说明
6.示例代码
alarm() ------ 定时给自身发送SIGALRM信号
1.头文件
2.函数原型
3.函数参数
4.返回值
5.函数功能与说明
6.示例代码
三、等待接收信号
pause()
1.头文件
2.函数原型
3.函数参数
4.返回值
5.功能与说明
6.示例代码
四、处理信号
signal() ------ 设置信号处理
1.头文件
2.函数原型
3.函数参数
4.返回值
5.功能与说明
五、综合示例
触发信号的方式
一、信号的概述
信号的本质
在 Linux 系统 中,信号(Signal)本质上是一种软中断机制(Software Interrupt),用于通知进程发生了某种异步事件。
它是 软件层面对硬件中断机制的模拟,在概念上,一个进程接收到信号与处理器接收到硬件中断请求非常相似。
信号是 进程间通信(IPC)的一种异步通信机制:
-
进程在执行过程中无需主动等待信号的到来;
-
信号可能在任何时刻由内核或其他进程异步地发送过来;
-
被信号打断后,进程会根据预设的处理方式(默认处理、忽略、自定义函数)来应对该事件。
除了基本的事件通知功能外,信号机制还具备一定的控制与管理能力,例如:
-
可以控制进程的执行状态(终止、暂停、恢复等);
-
可以实现进程间的事件同步与异常响应。
信号的来源
信号的产生途径多种多样,根据产生条件的不同,可以大体分为 两类:硬件方式 和 软件方式。
一、硬件方式产生的信号(Hardware-Generated Signals)
硬件事件在运行过程中触发信号,通常由 内核检测到异常或外部输入事件 所引起。常见情况包括:
-
1.键盘操作触发
-
用户在终端输入特定组合键时,终端驱动程序会向前台进程发送信号。
例如:
-
Ctrl + C
→ 发送 SIGINT(中断信号),终止前台进程。 -
Ctrl + Z
→ 发送 SIGTSTP(暂停信号),挂起前台进程。
-
-
-
2.程序运行错误触发
-
当程序在运行中出现异常或非法操作时,内核会自动向该进程发送信号:
-
空指针访问、非法内存访问 → SIGSEGV(段错误)
int *p = NULL; *p = 12456; // 触发段错误,进程被内核终止
-
除以 0 → SIGFPE(算术错误信号)
-
执行非法指令 → SIGILL
-
-
二、软件方式产生的信号(Software-Generated Signals)
软件层面可以通过系统调用或命令来产生信号,这些信号通常由进程主动触发或系统函数自动触发。
-
命令触发
-
通过命令行工具向进程发送信号,例如:
kill -9 1234 # 向 PID 为 1234 的进程发送 SIGKILL 信号
(内部实际上是调用了
kill()
系统调用。)
-
-
函数触发
-
程序中可以通过以下函数主动发送信号:
kill(pid_t pid, int sig); // 向指定进程发送信号 raise(int sig); // 向当前进程发送信号 sigqueue(pid_t pid, int sig, const union sigval value); // 带参数发送信号
-
示例:
kill(getpid(), SIGINT); // 向自己发送中断信号
-
信号的种类,以及处理流程
Linux 系统中的信号可以从不同角度进行分类,主要包括 按可靠性分类 和 按时间特性分类。
一、按可靠性分类
1️⃣ 不可靠信号(Non-Reliable Signals)
-
早期 UNIX 系统 设计较为简单,信号机制存在缺陷:
当同一种信号在尚未处理完时再次到达,后续信号可能被丢失。 -
因此,这类可能丢失的信号被称为 “不可靠信号”。
-
在 Linux 中,信号编号 1~31 的传统信号属于不可靠信号。
特点:
-
不支持信号排队(同类型信号可能被覆盖)
-
信号值固定,语义预定义(例如 SIGINT、SIGKILL 等)
2️⃣ 可靠信号(Reliable Signals)
-
为解决信号丢失问题,Linux 在原有信号机制基础上扩展了一组新的信号,称为 可靠信号。
-
这类信号支持 信号排队机制:
当一个信号正在被处理时,如果再次收到同类信号,会进入信号队列中,等待前一个信号处理完成后依次处理。 -
因此,不会发生信号丢失的情况。
范围:
-
可靠信号编号从
SIGRTMIN
~SIGRTMAX
(一般为 34~64)。
特点:
-
支持排队(不会丢失)
-
可附带参数(通过
sigqueue()
发送) -
信号含义可由用户自定义
二、按时间特性分类
1️⃣ 非实时信号(Non-Real-Time Signals)
-
非实时信号即早期的 不可靠信号。
-
每个信号都有固定含义,系统预定义行为,例如:
-
SIGINT
:中断进程 -
SIGKILL
:强制结束进程
-
-
不支持排队机制,如果在信号处理期间收到相同信号,后者会被忽略。
总结:
非实时信号的特点是:
“固定含义,不支持排队,可能丢失”
2️⃣ 实时信号(Real-Time Signals)
-
实时信号是 POSIX 标准 新增的信号类型,属于可靠信号的一种。
-
允许用户自定义信号编号及其处理逻辑。
-
具有 排队 和 优先级 特性:
-
多个相同信号可以依次排队执行;
-
编号越小的实时信号优先级越高;
-
可以使用
sigqueue()
附带数据发送。
-
总结:
实时信号的特点是:
“可自定义,支持排队,不丢失,具备优先级”
信号列表
信号名 | 数值 | 英文名称 | 默认行为 | 说明 |
---|---|---|---|---|
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 | 默认终止 | 实时信号(可自定义,用于线程或实时通信) |
键盘产生的信号
在 Linux 系统中,用户通过 键盘组合键 可以向 前台进程组(Foreground Process Group) 发送特定信号。
这些信号由 终端驱动程序(Terminal Driver) 捕获并传递给相关进程,用于中断、暂停或终止当前运行的程序。
一、常见的键盘控制信号
键盘操作 | 产生的信号 | 作用对象 | 默认处理行为 | 说明 |
---|---|---|---|---|
Ctrl + C | SIGINT | 前台进程组中的所有进程 | 终止进程 | 常用于终止正在运行的程序(Interrupt) |
Ctrl + Z | SIGTSTP | 前台进程组中的所有进程 | 挂起进程 | 暂停当前进程(Stop),可用 fg 或 bg 恢复 |
Ctrl + \ | SIGQUIT | 前台进程组中的所有进程 | 终止进程并生成 core 文件 | 常用于调试,程序异常退出时产生 core dump |
Ctrl + D | (非信号)EOF | 输入结束 | 关闭标准输入流 | 并非信号,而是表示输入流结束(End Of File) |
二、信号说明
-
1.Ctrl + C → SIGINT(中断信号)
-
向前台进程组发送中断信号。
-
默认行为:终止所有前台进程。
-
可通过
signal(SIGINT, handler)
捕获并自定义处理。 -
示例:
./a.out ^C # 用户按下 Ctrl+C,进程被终止
-
-
Ctrl + Z → SIGTSTP(暂停信号)
-
向前台进程组发送暂停信号。
-
默认行为:挂起(暂停)进程,可通过
fg
恢复至前台或bg
放入后台运行。 -
示例:
./a.out ^Z [1]+ Stopped ./a.out
-
-
Ctrl + \ → SIGQUIT(退出信号)
-
向前台进程组发送退出信号。
-
默认行为:终止进程并生成 core dump 文件,用于调试程序异常。
-
常用于调试运行时错误或崩溃。
-
三、总结对比
操作键 | 信号名称 | 信号编号 | 默认动作 | 是否可捕获 | 说明 |
---|---|---|---|---|---|
Ctrl + C | SIGINT | 2 | 终止进程 | ✅ 可捕获 | 常用的中断信号 |
Ctrl + Z | SIGTSTP | 20 | 暂停进程 | ✅ 可捕获 | 可用 fg/bg 恢复 |
Ctrl + \ | SIGQUIT | 3 | 终止并生成 core | ✅ 可捕获 | 用于调试 |
Ctrl + D | EOF(非信号) | — | 输入结束 | ❌ | 表示输入流结束 |
信号的处理方式
在 Linux 系统中,每个信号都可以被进程以不同方式处理,主要分为 三类:
1️⃣ 默认处理(SIG_DFL)
-
含义:使用系统为该信号预定义的默认动作。
-
特点:
-
对大多数信号,默认动作是 终止进程;
-
对少数信号,默认动作可能是 忽略信号 或 停止进程。
-
-
示例:
#include <signal.h>
#include <stdio.h>int main() {signal(SIGINT, SIG_DFL); // Ctrl+C 将按默认方式终止进程while(1); // 无限循环等待信号return 0;
}
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;
}
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;
}
信号通信的流程
一个完整的信号生命周期可以分为 三个阶段,涉及 四个关键事件:
阶段一:信号产生(Signal Generation)
-
发生位置:内核(Kernel)
-
触发方式:
-
硬件触发:如空指针访问、非法指令、键盘组合键(Ctrl+C)
-
软件触发:如
kill()
、raise()
、sigqueue()
-
-
说明:
内核负责生成信号,并将其标记到目标进程的信号位图中,等待进程处理。
阶段二:信号在进程中注册(Signal Registration)
-
发生位置:用户进程(User Process)
-
操作:
-
进程通过
signal()
或sigaction()
注册信号处理函数 -
也可以选择忽略信号 (
SIG_IGN
) 或使用默认动作 (SIG_DFL
)
-
-
说明:
-
用户进程不能直接给其他用户进程发送信号,必须通过 内核
-
用户空间只负责告诉内核如何处理信号
-
阶段三:信号处理(Signal Handling)
-
发生位置:用户进程
-
操作:
-
当进程在用户态运行时,内核检查是否有待处理信号
-
若有,则暂停当前执行,调用注册的处理函数执行信号响应逻辑
-
信号处理完成后,恢复原来的程序执行
-
信号通信补充说明
-
用户进程不能直接向其他用户进程发送信号
-
必须通过内核转发
-
例子:
进程A --> 内核 --> 发送信号 --> 进程B
-
-
用户空间不具备发送信号的能力
-
进程只能将信号请求提交给内核,由内核完成实际发送
-
二、发送信号
kill() ------ 给任一进程/进程组发送任一信号
1.头文件
#include <sys/types.h>
#include <signal.h>
2.函数原型
int kill(pid_t pid, int sig);
3.参数说明
参数名 | 类型 | 说明 |
---|---|---|
pid | pid_t | 指定要发送信号的目标进程或进程组: • • • • |
sig | int | 要发送的信号编号,例如: • • • • |
4.返回值
返回值 | 含义 |
---|---|
0 | 信号发送成功 |
-1 | 发送失败(常见错误:进程不存在或无权限) |
常见错误号:
-
ESRCH
:指定的进程不存在; -
EPERM
:没有权限向该进程发送信号。
kill()
函数与 kill
命令的区别
对比项 | kill() 函数 | kill 命令 |
---|---|---|
使用位置 | C 语言程序中调用 | 在终端命令行中使用 |
参数顺序 | kill(pid, sig) → 先进程,后信号 | kill -sig pid → 先信号,后进程 |
示例 | kill(1234, 9); | kill -9 1234 |
5.示例代码
利用 kill()
函数结束进程
思路
-
第一个终端运行目标程序(例如
./pidtest
)。 -
第二个终端运行本程序,通过命令行参数发送信号结束目标进程。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>/*
使用方式:./a.out -9 1234
表示向 PID=1234 的进程发送 SIGKILL 信号
*/int main(int argc, char *argv[])
{pid_t pid;int sig;if (argc != 3) {printf("用法: %s -sig pid\n", argv[0]);printf("示例: %s -9 1234\n", argv[0]);return -1;}// 将 "-9" 转换为信号编号 9sig = atoi(argv[1]) * (-1);pid = (pid_t)atoi(argv[2]);if (kill(pid, sig) == 0) {printf("成功向进程 %d 发送信号 %d\n", pid, sig);} else {perror("kill 发送失败");}return 0;
}
现象:
1.
2.
raise() ------ 给当前线程发送任一信号
1.头文件
#include <signal.h>
2.函数原型
int raise(int sig);
3.函数参数
-
sig:要发送的信号编号(例如
SIGINT
、SIGTERM
、SIGKILL
等)。
表示将该信号发送给调用本函数的进程本身。
常用信号:
信号宏 | 数值 | 含义 |
---|---|---|
SIGINT | 2 | 中断信号(如 Ctrl + C) |
SIGTERM | 15 | 终止信号(可捕获、可处理) |
SIGKILL | 9 | 强制杀死(不可捕获、不可忽略) |
SIGSEGV | 11 | 段错误 |
SIGABRT | 6 | 调用 abort() 产生的信号 |
4.返回值
-
成功:返回
0
-
失败:返回非 0 值
5.功能与说明
raise()
函数用于向当前进程发送信号。
可以理解为:
“自己给自己发一个信号。”
相当于:
kill(getpid(), sig);
6.示例代码
#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;
}
现象:
alarm() ------ 定时给自身发送SIGALRM信号
1.头文件
#include <unistd.h>
2.函数原型
unsigned int alarm(unsigned int seconds);
3.函数参数
seconds:指定 多少秒后 向当前进程发送 SIGALRM
信号。
-
若
seconds = 0
:取消之前设置的定时器(不再发送信号)。 -
若非 0:在指定秒数后系统自动给本进程发送
SIGALRM
信号。
4.返回值
返回值为:
-
0:表示之前没有设置定时器;
-
非 0:返回 上一次定时器剩余的秒数。
5.函数功能与说明
-
alarm()
用于设置一个定时信号(定时器)。 -
当设置的时间到达后,系统自动向当前进程发送
SIGALRM
信号。 -
可以配合
signal()
使用来自定义信号处理函数。 -
一个进程同一时间只能有一个
alarm
定时器。
再次调用会覆盖上一次设置。
6.示例代码
#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;
}
现象:
三、等待接收信号
pause()
1.头文件
#include <unistd.h>
2.函数原型
int pause(void);
3.函数参数
- 无参数
4.返回值
情况 | 返回值 | 说明 |
---|---|---|
正常情况下 | 不返回 | 程序被挂起(等待信号时不会返回) |
被信号唤醒后 | -1 | 函数被中断,返回 -1,并将 errno 设为 EINTR |
5.功能与说明
-
pause()
使进程进入睡眠状态,直到接收到一个信号。 -
当信号被捕获并执行完信号处理函数后,
pause()
才会返回。 -
常用于等待定时信号或外部信号。
-
通常和
alarm()
、signal()
搭配使用。
6.示例代码
#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;
}
现象:
四、处理信号
signal() ------ 设置信号处理
1.头文件
#include <signal.h>
2.函数原型
void (*signal(int signum, void (*handler)(int)))(int);
虽然原型比较复杂,但可以理解为:
signal(信号编号, 信号处理函数);
3.函数参数
参数名 | 类型 | 说明 |
---|---|---|
int signum | 信号编号 | 指定要处理的信号编号(如 SIGINT , SIGALRM , SIGTERM 等)。 |
sighandler_t handler | 信号处理方式 | 设置该信号的处理方法,常见三种: |
三种处理方式如下:
处理方式 | 含义 |
---|---|
SIG_DFL | 对该信号采取 默认的处理方式 |
SIG_IGN | 忽略 该信号(不做任何反应) |
自定义函数名(如 sig_handler ) | 指定自定义的信号处理函数(函数声明格式如下) |
⚠️ 信号处理函数声明格式:
void sigX_handler(int arg);
当捕获到信号时,系统会自动调用此函数,并将信号编号作为参数传入。
4.返回值
-
成功:返回先前的信号处理函数指针;
-
失败:返回
SIG_ERR
。
5.功能与说明
signal()
函数用于设置当前进程对指定信号的处理方式。
当进程接收到信号 signum
时,系统根据 handler
指定的方式进行处理:
-
若设置为
SIG_DFL
→ 使用系统默认处理; -
若设置为
SIG_IGN
→ 忽略信号; -
若设置为自定义函数 → 调用该处理函数。
五、综合示例
综合案例1:0~5s发送系统信号2 忽略 5~10秒发送信号2 执行处理函数的内容 10秒后执行信号默认功能
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void sigint_handler(int signo) {printf("捕获到信号 SIGINT(Ctrl+C),执行自定义处理函数!\n");
}int main(void) {printf("程序开始运行...\r\n");printf("0-5s忽略SIGINT信号\r\n");signal(SIGINT, SIG_IGN);for(int i=0; i<5; i++) {printf("time is %ds\r\n", i);sleep(1);}printf("5-10s执行自定义处理函数\r\n");signal(SIGINT, sigint_handler);for(int i=5; i<10; i++) {printf("time is %ds\r\n", i);sleep(1);}printf("10s后执行信号默认功能\r\n");signal(SIGINT, SIG_DFL);int i=10;while (1) {printf("time is %ds\r\n", i++);sleep(1);}}
现象:
综合案例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); // 信号 2signal(SIGQUIT, signal_handler); // 信号 3signal(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:每隔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;}
现象: