linux中信号解析
1. 信号的基本概念
1.1 什么是信号?
信号是一种软件中断,用于通知进程发生了某个事件。信号可以由内核、另一个进程或进程自身发送。信号通常用于以下目的:
- 通知错误:如非法内存访问(
SIGSEGV
)。 - 控制进程:如终止进程(
SIGTERM
)、暂停进程(SIGSTOP
)。 - 用户交互:如用户按下
Ctrl+C
发送SIGINT
信号。
1.2 信号的分类
- 标准信号:传统的信号,如
SIGINT
、SIGTERM
、SIGSEGV
等。 - 实时信号(可靠信号):信号编号从
SIGRTMIN
到SIGRTMAX
,可以排队处理,不会丢失。
2. 常见的信号
以下是一些常见的Linux信号及其含义:
3. 信号的处理方式
每个信号都有其默认的处理方式,进程可以通过以下方式改变信号的处理方式:
3.1 默认处理方式
- Terminate:终止进程。
- Ignore:忽略信号。
- Core Dump:生成核心转储文件并终止进程。
- Stop:停止进程。
- Continue:继续进程。
3.2 使用 signal
函数
signal
函数用于设置信号的处理方式,但其行为在不同系统上可能有所不同,且不具备可重入性
参数解释:
-
signum
:- 含义:指定要处理的信号类型。例如,
SIGINT
(信号值为2)表示中断信号,通常由用户按下Ctrl+C
触发。 - 取值范围:通常使用标准信号(如
SIGINT
、SIGTERM
等)或实时信号(SIGRTMIN
到SIGRTMAX
)。
- 含义:指定要处理的信号类型。例如,
-
sighandler_t handler
:- 含义:指定信号的处理函数。当信号
signum
发生时,将调用该函数。 - 类型:
sighandler_t
是一个函数指针,指向一个返回类型为void
且接受一个int
参数的函数。该参数为触发信号的信号编号。 - 特殊值:
SIG_IGN
:忽略该信号。SIG_DFL
:恢复信号的默认处理方式。
- 含义:指定信号的处理函数。当信号
例子:
#include <signal.h>
#include <stdio.h>void handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {if (signal(SIGINT, handler) == SIG_ERR) {perror("signal");return 1;}while (1) {printf("Waiting for signal...\n");sleep(1);}return 0;
}
3.3 使用 sigaction
函数
sigaction
提供了更可靠和可重入的信号处理方式,推荐使用。
参数解释:
-
signum
:- 含义:和上面一致
-
const struct sigaction *act
:- 含义:指向一个
struct sigaction
结构体,该结构体指定了新的信号处理方式。 - 结构体成员:
sa_handler
:类似于signal
函数中的handler
,指定信号处理函数或SIG_IGN
、SIG_DFL
。sa_mask
:一个信号集,指定在执行信号处理函数期间要屏蔽的额外信号。sa_flags
:一组标志,用于控制信号处理的行为。例如,SA_RESTART
表示在信号处理后重启被中断的系统调用。
- 含义:指向一个
-
struct sigaction *oldact
:- 含义:指向一个
struct sigaction
结构体,用于保存之前的信号处理方式。如果不需要保存,可以传递NULL
。
- 含义:指向一个
例子:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {struct sigaction sa;sa.sa_handler = handler;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction");return 1;}while (1) {printf("Waiting for signal...\n");sleep(1);}return 0;
}
4. 信号屏蔽与等待
4.1 信号屏蔽
使用sigprocmask
函数可以临时屏蔽某些信号,直到主程序完成关键操作。
参数解释:
-
how
:- 含义:指定如何修改当前的信号屏蔽集。
SIG_BLOCK
:将set
中的信号添加到当前信号屏蔽集中。SIG_UNBLOCK
:从当前信号屏蔽集中移除set
中的信号。SIG_SETMASK
:将当前信号屏蔽集设置为set
。
- 含义:指定如何修改当前的信号屏蔽集。
-
const sigset_t *set
:- 含义:指向一个信号集,指定要修改的信号。
- 特殊值:如果
set
为NULL
,则how
参数被忽略,oldset
将包含当前的信号屏蔽集。
-
sigset_t *oldset
:- 含义:指向一个信号集,用于保存之前的信号屏蔽集。如果不需要保存,可以传递
NULL
。
- 含义:指向一个信号集,用于保存之前的信号屏蔽集。如果不需要保存,可以传递
例子:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {sigset_t mask, oldmask;struct sigaction sa;sa.sa_handler = handler;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction");return 1;}// 初始化信号集并添加SIGINT到屏蔽集中sigemptyset(&mask);sigaddset(&mask, SIGINT);if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1) {perror("sigprocmask");return 1;}// 执行关键操作printf("关键操作开始,SIGINT被屏蔽\n");sleep(5);printf("关键操作结束,恢复SIGINT\n");// 恢复之前的信号屏蔽if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {perror("sigprocmask");return 1;}while (1) {printf("Waiting for signal...\n");sleep(1);}return 0;
}
4.2 信号等待
使用sigsuspend
函数可以等待特定信号的发生。
参数解释:
const sigset_t *mask
:- 含义:指向一个信号集,指定在等待信号期间要临时设置的信号屏蔽集。
- 行为:该函数会原子性地将当前的信号屏蔽集替换为
mask
,然后挂起进程,直到接收到一个未屏蔽的信号。接收到信号后,进程的信号屏蔽集会恢复到原来的状态。
例子:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {sigset_t mask, oldmask;struct sigaction sa;sa.sa_handler = handler;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction");return 1;}// 初始化信号集并添加SIGINT到屏蔽集中sigemptyset(&mask);sigaddset(&mask, SIGINT);if (sigprocmask(SIG_BLOCK, &mask, &oldmask) == -1) {perror("sigprocmask");return 1;}// 等待信号printf("等待信号...\n");sigsuspend(&oldmask);// 恢复信号屏蔽if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1) {perror("sigprocmask");return 1;}return 0;
}