Linux信号的使用

✅ 信号的基本概念

🔹定义:信号是异步事件通知机制,用于进程间通信或内核通知进程某事件发生。

🔹常见信号:

SIGINT (2):终端中断(Ctrl+C)。

SIGKILL (9):强制终止进程(不可捕获或忽略)。

SIGTERM (15):请求进程终止(可处理)。

SIGSEGV (11):段错误(非法内存访问)。

SIGCHLD (17):子进程状态改变(如终止)。

其他:SIGQUIT、SIGALRM、SIGPIPE等。


✅ 信号的产生方式

🔹用户输入:如Ctrl+C(SIGINT)、Ctrl+\(SIGQUIT)。

🔹系统调用/命令:kill()函数或kill命令发送信号。

🔹内核生成:如硬件异常(除零错误→SIGFPE)、子进程终止(SIGCHLD)。


✅ 信号的处理

🔹默认行为:终止进程(Term)、忽略(Ignore)、暂停(Stop)等。

🔹自定义处理:通过signal()或更安全的sigaction()注册处理函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void sigint_handler(int sig) {
write(STDOUT_FILENO, "\nCaught SIGINT! Exiting...\n", 25);
_exit(0); // 直接退出,避免标准库函数
}

int main() {
signal(SIGINT, sigint_handler);
while(1) sleep(1);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handler(int sig, siginfo_t *info, void *ucontext) {
printf("Received signal %d from PID %d\n", sig, info->si_pid);
}

int main() {
struct sigaction sa;
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO; // 允许携带额外信息
sigemptyset(&sa.sa_mask); // 清空信号屏蔽集

if (sigaction(SIGTERM, &sa, NULL) == -1) {
perror("sigaction");
return 1;
}

printf("PID: %d\n", getpid());
while(1) pause(); // 等待信号
}

🔹不可捕获的信号:SIGKILL和SIGSTOP不能被捕获或忽略。

✅ 信号阻塞与信号集

🔹信号集操作:sigemptyset、sigfillset、sigaddset、sigdelset。

🔹进程信号屏蔽字:通过sigprocmask()阻塞/解除阻塞信号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

int main() {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT); // 将SIGINT加入集合
sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT

printf("SIGINT is blocked. Try Ctrl+C...\n");
sleep(5);

sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除阻塞
printf("SIGINT unblocked. Now Ctrl+C will work.\n");
while(1) sleep(1);
}

🔹多线程:pthread_sigmask()设置线程级信号屏蔽。


✅ 可靠信号与实时信号

🔹不可靠信号(1~31):可能丢失,同种信号多次触发只处理一次。

🔹实时信号(SIGRTMIN~SIGRTMAX):支持排队,通过sigqueue()发送携带数据的信号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <signal.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <PID>\n", argv[0]);
return 1;
}

pid_t pid = atoi(argv[1]);
union sigval value;
value.sival_int = 5678;
sigqueue(pid, SIGRTMIN, value);
return 0;
}

✅ 关键函数与系统调用

🔹kill()/raise():发送信号到进程或自身。

🔹pause():挂起进程直到信号到达。

🔹sigsuspend():原子操作“解除信号阻塞 + 等待信号”。

🔹alarm()/setitimer():设置定时器触发SIGALRM。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void alarm_handler(int sig) {
printf("Timeout! Exiting...\n");
_exit(1);
}

int main() {
signal(SIGALRM, alarm_handler);
alarm(5); // 5秒后触发SIGALRM
printf("Waiting for input (you have 5 seconds)...\n");
getchar(); // 若用户未输入,超时退出
printf("Input received.\n");
return 0;
}

🔹sigwait():同步等待信号(多线程中常用)。


Linux信号的使用
https://chrisy0618.github.io/2025/04/19/signal/
作者
Chris.Y
发布于
2025年4月19日
许可协议