signal
或 sigaction
函数来指定处理函数。在Linux系统中,信号是进程间通信的一种重要机制,通过信号,可以通知进程发生了某些事件,例如用户输入、定时器到期或者系统资源限制等,为了处理这些信号,Linux提供了信号捕捉机制,使得进程能够对特定的信号做出响应,本文将详细介绍Linux信号捕捉的概念、使用方法以及相关示例。
信号的基本概念
在Linux中,信号是一种异步通知机制,用于告知进程发生了某种事件,常见的信号包括:
SIGINT
:中断信号,通常由Ctrl+C产生。
SIGTERM
:终止信号,用于请求进程正常退出。
SIGKILL
:强制终止信号,用于立即终止进程。
SIGCHLD
:子进程状态改变信号。
信号捕捉的函数
在Linux中,可以使用signal()
或sigaction()
函数来捕捉和处理信号,这两个函数各有优缺点,但sigaction()
更为灵活和强大。
2.1 signal()函数
signal()
函数用于设置信号处理器,其原型如下:
#include <signal.h> void (*signal(int sig, void (*handler)(int)))(int);
参数说明:
sig
:要捕捉的信号。
handler
:信号处理函数。
示例代码:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void handle_sigint(int sig) { printf("Caught signal %d ", sig); exit(0); } int main() { signal(SIGINT, handle_sigint); while (1) { printf("Running... "); sleep(1); } return 0; }
2.2 sigaction()函数
sigaction()
函数提供了更细粒度的信号控制,其原型如下:
#include <signal.h> int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
参数说明:
sig
:要捕捉的信号。
act
:指向struct sigaction
结构的指针,定义了信号处理行为。
oldact
:指向存储旧的信号处理行为的struct sigaction
结构。
struct sigaction
结构定义如下:
struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int, siginfo_t *, void *); };
示例代码:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <unistd.h> void handle_sigint(int sig, siginfo_t *si, void *unused) { printf("Caught signal %d ", sig); exit(0); } int main() { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = handle_sigint; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, NULL) == -1) { perror("sigaction"); exit(1); } while (1) { printf("Running... "); sleep(1); } return 0; }
信号处理的注意事项
在使用信号处理时,需要注意以下几点:
1、可重入性:信号处理函数应尽量简单,避免调用不可重入的函数(如printf()
),推荐使用write()
系统调用。
2、阻塞其他信号:在信号处理函数执行期间,可以阻塞其他信号以避免竞态条件。
3、信号处理函数的返回值:如果信号处理函数返回,默认行为将被取消,因此通常需要调用exit()
来终止程序。
4、信号的安全性:某些信号(如SIGKILL
和SIGSTOP
)不能被捕捉和忽略。
常见信号及其用途
信号名称 | 数值 | 描述 |
SIGINT | 2 | 中断信号,通常由Ctrl+C产生 |
SIGTERM | 15 | 终止信号,用于请求进程正常退出 |
SIGKILL | 9 | 强制终止信号,用于立即终止进程 |
SIGCHLD | 17 | 子进程状态改变信号 |
SIGHUP | 1 | 挂起信号,通常在终端关闭时发送给前台进程 |
SIGQUIT | 3 | 退出信号,通常由Ctrl+\产生 |
SIGALRM | 14 | 定时器到期信号 |
SIGUSR1 | 10 | 用户定义信号1 |
SIGUSR2 | 12 | 用户定义信号2 |
实战示例:实现一个简单的定时器
下面是一个使用alarm()
和signal()
实现的简单定时器示例:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> void timer_handler(int sig) { static int count = 0; printf("Timer expired %d times ", ++count); alarm(1); // reset the timer to 1 second } int main() { signal(SIGALRM, timer_handler); // set up the timer handler alarm(1); // schedule the first timer expiration in 1 second while (1) { pause(); // wait for signals } return 0; }
FAQs
Q1: 如何在信号处理函数中使用printf()
?
A1: 在信号处理函数中使用printf()
是不安全的,因为它不是可重入的,推荐使用write()
系统调用代替:
#include <unistd.h> void safe_printf(const char *str) { write(STDOUT_FILENO, str, strlen(str)); }
Q2: 如何忽略一个信号?
A2: 可以通过将信号处理函数设为SIG_IGN
来忽略一个信号:
#include <signal.h> signal(SIGINT, SIG_IGN); // ignore SIGINT signal
本文介绍了Linux信号捕捉的基本概念、使用方法以及相关的注意事项,通过使用signal()
和sigaction()
函数,我们可以捕捉并处理各种信号,从而实现对特定事件的响应,在实际开发中,合理使用信号处理可以提高程序的健壮性和响应能力。
小伙伴们,上文介绍了“linux 信号捕捉”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。