在Linux系统编程中,信号是一种重要的机制,用于实现进程间通信和控制。当某个事件发生时,如用户按下Ctrl+C键,操作系统会向进程发送一个信号,进程可以捕获并相应地处理该信号。本篇博客将介绍信号的分类、捕获与处理方式,以及信号的默认操作。希望通过本篇博客,你能够更好地理解Linux系统编程中信号的概念与应用。
文章目录
-
- [1. 什么是信号?](#1. 什么是信号? "#1__7")
- [2. 信号的分类](#2. 信号的分类 "#2__14")
- [3. 信号的处理方式](#3. 信号的处理方式 "#3__23")
- [4. 信号的捕获与处理](#4. 信号的捕获与处理 "#4__34")
-
- [4.1 `signal`](#4.1
signal
"#41_signal_35") - [4.2 `sigaction`](#4.2
sigaction
"#42_sigaction_83")
- [4.1 `signal`](#4.1
- [5. 信号的默认操作](#5. 信号的默认操作 "#5__139")
- 总结
1. 什么是信号?
在Linux系统中,信号是一种用于进程间通信和控制的机制。当某个事件发生时,内核会向进程发送一个信号,进程可以选择捕获并处理这个信号,或者使用默认的处理方式。
信号可以由多种事件触发,例如用户按下Ctrl+C
键、进程发生错误、子进程退出等。每个信号都有一个唯一的编号,以及一个预定义的默认行为。
2. 信号的分类
- 标准信号(Standard Signals) :这些信号的编号在1到31之间,是POSIX标准中定义的。例如,SIGINT信号(编号为2)是由用户按下Ctrl+C键触发的,SIGTERM信号(编号为15)是用于终止进程的信号。
- 实时信号(Real-time Signals) :这些信号的编号在32到63之间,也是POSIX标准中定义的。实时信号提供了更高的优先级和更精确的触发时间。
- 自定义信号(User-defined Signals) :这些信号可以由用户自定义,其编号大于64。
3. 信号的处理方式
- 忽略(Ignore) :进程可以选择忽略某个特定的信号,这样当该信号发生时,进程不会做任何处理。但是,某些信号是不能被忽略的,例如SIGKILL和SIGSTOP。
- 捕获(Catch) :进程可以通过注册信号处理函数来捕获某个信号。当信号发生时,内核会调用注册的信号处理函数来处理该信号。
- 执行默认操作(Default Action) :每个信号都有一个预定义的默认行为,例如终止进程、终止进程并生成核心转储文件等。
4. 信号的捕获与处理
4.1 signal
作用 :signal
函数用于捕获和处理信号。
原型:
c
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
参数:
signum
:要捕获的信号编号。handler
:信号处理函数的指针。
返回值 :返回之前对该信号的处理函数的指针。如果出错,则返回SIG_ERR
。
示例 :下面是一个示例,演示如何使用signal
函数来捕获和处理SIGINT信号(用户按下Ctrl+C键)。
c
#include <stdio.h>
#include <signal.h>
void sigint_handler(int signum) {
printf("Received SIGINT signal\n");
}
int main() {
// 注册SIGINT信号的处理函数
signal(SIGINT, sigint_handler);
printf("Press Ctrl+C to send SIGINT signal\n");
while (1) {
// 无限循环,等待信号的发生
}
return 0;
}
示例解释:
- 在上面的示例中,我们定义了一个名为
sigint_handler
的信号处理函数。当接收到SIGINT信号时,该函数会被调用,并打印一条消息。 - 在
main
函数中,我们使用signal
函数来注册sigint_handler
函数作为SIGINT信号的处理函数。这样,当用户按下Ctrl+C键时,操作系统会发送SIGINT信号给进程,并调用sigint_handler
函数来处理该信号。 - 在
main
函数的无限循环中,我们等待信号的发生。这样,进程会一直运行,直到接收到SIGINT信号或其他终止信号为止。 - 当我们按下Ctrl+C键时,会触发SIGINT信号,进程会调用
sigint_handler
函数,并打印一条消息。
4.2 sigaction
作用 :sigaction
函数是另一种捕获和处理信号的方法,相比于signal
函数,它提供了更多的灵活性和可靠性。
原型:
c
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数:
signum
:要捕获的信号编号。act
:一个指向struct sigaction
结构的指针,用于指定新的信号处理方式。oldact
:一个指向struct sigaction
结构的指针,用于保存之前的信号处理方式。
返回值:若成功,返回0;若出错,返回-1。
示例 :下面是一个示例,演示如何使用sigaction
函数来捕获和处理SIGINT信号(用户按下Ctrl+C键)。
c
#include <stdio.h>
#include <signal.h>
void sigint_handler(int signum) {
printf("Received SIGINT signal\n");
}
int main() {
struct sigaction sa;
sa.sa_handler = sigint_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
// 注册SIGINT信号的处理函数
sigaction(SIGINT, &sa, NULL);
printf("Press Ctrl+C to send SIGINT signal\n");
while (1) {
// 无限循环,等待信号的发生
}
return 0;
}
示例解释:
- 在上面的示例中,我们定义了一个名为
sigint_handler
的信号处理函数。当接收到SIGINT信号时,该函数会被调用,并打印一条消息。 - 我们创建了一个
struct sigaction
结构sa
,并将sigint_handler
函数指定为信号处理函数。 - 使用
sigemptyset
函数初始化了sa.sa_mask
,表示在处理SIGINT信号时不需要阻塞其他信号。 sa.sa_flags
设置为0,表示不需要特殊的标志。- 使用
sigaction
函数将sa
结构注册为SIGINT信号的处理方式。 - 在
main
函数的无限循环中,我们等待信号的发生。这样,进程会一直运行,直到接收到SIGINT信号或其他终止信号为止。 - 当我们按下Ctrl+C键时,会触发SIGINT信号,进程会调用
sigint_handler
函数,并打印一条消息。
5. 信号的默认操作
如果不捕获某个信号或者捕获的信号处理函数返回,进程会执行该信号的默认操作。
例如,当接收到SIGTERM信号时,进程会默认终止。下面是一个示例,演示了SIGTERM信号的默认操作:
c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sigterm_handler(int signum) {
printf("Received SIGTERM signal\n");
}
int main() {
// 注册SIGTERM信号的处理函数
signal(SIGTERM, sigterm_handler);
printf("Press Ctrl+C to send SIGTERM signal\n");
while (1) {
// 无限循环,等待信号的发生
}
return 0;
}
在上面的示例中,我们捕获了SIGTERM信号并注册了sigterm_handler
函数作为处理函数。当接收到SIGTERM信号时,该函数会被调用,并打印一条消息。
总结
信号是Linux系统中用于进程间通信和控制的机制。我们可以通过捕获信号并注册处理函数来实现对信号的处理。在处理信号时,我们可以选择忽略、捕获或执行默认操作。