一、 什么是进程信号?
信号(Signal)是 Linux 操作系统中用于进程间异步通知 的一种机制,属于软中断。
就像快递员给你送快递,你不需要一直在门口等着,快递到了会打电话通知你。进程收到信号后,有三种处理方式:
-
默认动作(SIG_DFL):系统预设的操作,如 Ctrl+C 就是默认终止。
-
忽略(SIG_IGN):收到后当作没看见。
-
自定义捕捉:执行程序员指定的自定义函数。
二、 信号的产生方式
-
终端按键 :如
Ctrl+C(SIGINT, 2号)、Ctrl+\(SIGQUIT, 3号)、Ctrl+Z(SIGTSTP, 20号)。 -
系统调用:
-
kill(pid, sig):给指定进程发信号。 -
raise(sig):给自己发信号。 -
abort():给自己发 SIGABRT 信号,异常终止。
-
-
软件条件 :
alarm(seconds)。调用后设置一个定时器,时间一到,内核给该进程发送 SIGALRM (14号) 信号。 -
硬件异常:除零错误 (SIGFPE, 8号)、非法内存访问/野指针 (SIGSEGV, 11号)。
三、 实战:自定义信号捕捉
使用 signal(int signum, handler) 函数可以改变信号的处理动作。
cpp
void my_handler(int signo) {
printf("捕捉到信号: %d\n", signo);
}
int main() {
signal(SIGINT, my_handler); // 拦截 Ctrl+C
while(1) {
printf("我在死循环里等信号...\n");
sleep(1);
}
}

在这个实战中,进程对 Ctrl+C 产生了免疫。如果真的要退出,需要发送 9号信号 (SIGKILL),这是一种绝对无法被捕捉和忽略的终极杀招。
四、 pause() 的妙用
pause() 函数会让当前进程进入无 CPU 消耗的挂起状态(S状态),直到进程收到一个由信号捕捉函数处理并返回的信号为止。非常适合用来做基于信号驱动的异步程序。
总结
信号是操作系统的神经系统。理解信号的自定义捕捉,是编写健壮的服务端程序(如处理优雅退出)的必备基
自我实战操作
咱们建个新目录来玩信号。
bash
cd ~/linux_study
mkdir signal_test
cd signal_test
实战 1:让进程"刀枪不入"w
bash
vim sig_catch.c
(输入以下代码)
cpp
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
// 自定义信号处理函数
void my_handler(int signo) {
printf("\n[捕捉到信号] 编号: %d, 我是不会退出的!\n", signo);
}
int main() {
// 注册捕捉函数:收到 2号信号(SIGINT, 即Ctrl+C) 时,执行 my_handler
// 如果写 signal(SIGINT, SIG_IGN); 就是忽略该信号
signal(SIGINT, my_handler);
while(1) {
printf("我在死循环里等信号...\n");
sleep(1);
}
return 0;
}
编译并运行:
bash
bash
gcc sig_catch.c -o sig_catch
./sig_catch
👉 现象 :你在终端里猛按 Ctrl+C,它不但不会死,还会打印出你自定义的提示语!
👉 怎么退出? :因为 Ctrl+C 被我们拦截了,你得按 Ctrl+\(发送3号信号 SIGQUIT),或者新开一个终端用 kill -9 进程PID 杀掉它。
实战 2:体验系统调用 kill 和 raise 以及 alarm
bash
vim sig_alarm.c
(输入以下代码)
cpp
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void handler_alarm(int signo) {
printf("闹钟响了!收到信号: %d\n", signo);
exit(0);
}
int main() {
signal(SIGALRM, handler_alarm); // 捕捉闹钟信号
alarm(3); // 3秒后触发 SIGALRM
printf("我给自己设了个3秒的闹钟,自己给自己发信号...\n");
while(1) {
pause(); // 将进程挂起,直到收到信号
}
return 0;
}
编译运行:
bash
gcc sig_alarm.c -o sig_alarm
./sig_alarm

👉 现象 :程序卡住 3 秒,然后打印"闹钟响了",接着退出。pause() 是个非常牛的函数,它会让进程进入睡眠状态,一点 CPU 都不占,直到被信号"叫醒"。
实战 3:模拟野指针崩溃信号
bash
vim sig_segv.c
(输入以下代码)
cpp
#include <stdio.h>
#include <signal.h>
void handler(int signo) {
printf("抓到了!信号编号: %d (段错误)\n", signo);
}
int main() {
signal(SIGSEGV, handler); // 捕捉段错误信号 (11号)
int *p = NULL;
*p = 100; // 故意触发野指针访问异常,OS会发 SIGSEGV
return 0;
}

(编译运行,你会发现原本要直接崩溃的程序,打印出了"抓到了!",但是!它依然会崩溃。因为硬件异常哪怕你捕捉了,返回时还是会在那条非法指令处再次触发异常,形成死循环,OS最终还是会把它杀掉。这也是段错误很难"捕捉"着救回来的原因。)