在 Linux 中,信号(Signal)是一种进程间通信的机制,用于通知进程发生了某种事件。理解信号的来源对于开发可靠、健壮的程序至关重要。本文将介绍三种常见的信号产生方式,包括:kill 命令、键盘输入(如 Ctrl+C)、以及系统调用,并配上相关示例代码来帮助理解。
一、使用 kill 命令发送信号
kill 命令是最常见的发送信号方式,它通过向指定进程发送一个信号(默认是 SIGTERM,编号 15)来通知它终止或执行某些操作。
示例:
# 查看当前终端的 bash 进程号
$ echo $$
12345# 向该进程发送 SIGINT(中断信号,编号2)
$ kill -2 12345# 发送默认的 SIGTERM(终止信号)
$ kill 12345
kill -l 可以列出所有支持的信号及其编号;
kill -9 PID 发送 SIGKILL 信号,强制终止进程,不可被捕捉或忽略;
二、键盘输入产生的信号
键盘是最常见的交互方式,在终端中直接通过快捷键向前台进程发送信号。
常用快捷键及对应信号
快捷键 | 信号名称 | 信号编号 | 含义 |
---|---|---|---|
Ctrl + C | SIGINT | 2 | 中断进程 |
Ctrl + \ | SIGQUIT | 3 | 退出进程并产生 core dump |
Ctrl + Z | SIGTSTP | 20 | 暂停进程 |
示例代码(接收 SIGINT)
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void handle(int sig) {printf("收到信号 SIGINT(%d),中断操作被捕获!\n", sig);
}int main() {signal(SIGINT, handle); // 注册信号处理器while (1) {printf("程序运行中,按 Ctrl+C 发送 SIGINT 信号...\n");sleep(1);}return 0;
}
运行上述程序后按 Ctrl+C,你会看到自定义的中断提示,而不是程序直接退出。
三、系统调用产生信号(编程方式)
可以通过系统调用函数 kill() 或 raise() 在程序内部主动发送信号。
1、kill():向其他进程或自己发送信号
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>void handle_usr1(int sig) {printf("子进程收到 SIGUSR1 信号!\n");
}int main() {pid_t pid = fork();if (pid < 0) {perror("fork 失败");exit(1);}if (pid == 0) {// 子进程signal(SIGUSR1, handle_usr1);printf("子进程 PID: %d,等待父进程发信号...\n", getpid());pause(); // 等待信号} else {// 父进程sleep(2); // 给子进程注册 handler 的时间printf("父进程向子进程(PID: %d)发送 SIGUSR1...\n", pid);kill(pid, SIGUSR1);wait(NULL); // 等待子进程退出}return 0;
}
2、 raise():向自己发送信号
#include <signal.h>
#include <stdio.h>void handle_usr1(int sig) {printf("收到 SIGUSR1 信号(%d)\n", sig);
}int main() {signal(SIGUSR1, handle_usr1); // 注册信号处理器raise(SIGUSR1); // 向自身发送信号return 0;
}
因此:
kill() 适合发送信号到任意进程(需要权限);
raise() 适合程序内部自我触发信号,例如模拟中断或测试处理器行为。
四、总结
方式 | 说明 | 适用场景 |
---|---|---|
kill 命令 | 通过命令行发送信号 | 终端手动管理进程 |
键盘信号 | 快捷键控制前台进程 | 交互中断、终止 |
系统调用 | 程序内部控制信号行为 | 异常处理、调试 |