信号
一、 sigset_t
sigset_t
是一个数据类型,用于表示信号集。信号集本质上是一个信号的集合,用来指定多个信号。例如,您可以使用一个 sigset_t
变量来存储一组信号,然后使用该集合在诸如信号阻塞、信号等待等操作中。
1.1 使用方法
-
初始化信号集
在使用信号集之前,需要将其初始化为一个空集或包含所有信号的集合。这可以通过
sigemptyset
和sigfillset
函数来完成。sigset_t set; sigemptyset(&set); // 初始化为空集 sigfillset(&set); // 初始化为包含所有信号的集合
-
添加或者删除信号
使用
sigaddset
和sigdelset
函数可以向信号集中添加或删除特定的信号。sigaddset(&set, SIGINT); // 添加 SIGINT 信号到 set sigdelset(&set, SIGQUIT); // 从 set 中删除 SIGQUIT 信号 sigismember(&set, SIGINT);// 判断set中是否包含指定信号
-
修改进程的信号屏蔽字
sigprocmask
是Unix
和Linux
系统中用于修改当前进程的信号屏蔽字(signal mask)
的函数。信号屏蔽字是一个位掩码,它决定了哪些信号应当被当前进程阻塞,即不会被递送到进程进行处理。#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
-
参数:
how
:指定了如何修改当前进程的信号屏蔽字。它可以取以下三个值之一:SIG_BLOCK
:将set
所指向的信号集中所包含的信号加到当前的信号屏蔽字中。SIG_UNBLOCK
:将set
所指向的信号集中的信号从当前的信号屏蔽字中移除。SIG_SETMASK
:将当前进程的信号屏蔽字设置为set
所指向的信号集中所包含的信号。
set
:一个指向sigset_t
类型的指针,表示要修改或检测的信号集。如果该值为NULL
,则忽略该参数并不会改变当前进程的信号屏蔽集。oldset
:一个指向sigset_t
类型的指针,用于接收调用sigprocmask
之前的信号屏蔽字。如果此参数不为NULL
,则调用后将保存旧的信号屏蔽字到oldset
中。
-
返回值:
如果成功,则返回0;否则返回-1,并设置全局变量errno以指示错误。
-
-
示例:
#include <stdio.h> #include <signal.h> #include <stdlib.h> void sig_handler(int signo) { printf("Received signal %d\n", signo); exit(0); } int main() { sigset_t blockset, oldset; // 初始化blockset为空集 sigemptyset(&blockset); // 将SIGINT信号添加到blockset中 sigaddset(&blockset, SIGINT); // 绑定SIGINT信号的处理器 if (signal(SIGINT, sig_handler) == SIG_ERR) { perror("signal(SIGINT) error"); exit(1); } // 阻塞SIGINT信号 if (sigprocmask(SIG_BLOCK, &blockset, &oldset) < 0) { perror("sigprocmask(SIG_BLOCK) error"); exit(1); } // 在这里执行一些操作,SIGINT信号将被阻塞 // 恢复原来的信号屏蔽字 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) { perror("sigprocmask(SIG_SETMASK) error"); exit(1); } // 从这里开始,SIGINT信号将不再被阻塞 return 0; }
二、struct sigaction
2.1 结构介绍
struct sigaction
是一个结构体,用于指定当特定信号到达时应该如何处理它。这个结构体允许用户定义一个信号处理函数,以及在调用信号处理函数期间需要阻塞的信号集。它提供了比传统的 signal()
函数更多的控制和灵活性。
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
struct sigaction
结构体通常包括以下几个字段:
void (*sa_handler)(int)
- 这是指向信号处理函数的指针。该函数的类型为
void handler(int signum)
,其中signum
是被捕获信号的编号。 - 可以设置为
SIG_IGN
忽略信号,或SIG_DFL
使用默认的信号处理方式。
- 这是指向信号处理函数的指针。该函数的类型为
- sa_sigaction
- 这是另一种信号处理函数的指针,类型为
void handler(int signum, siginfo_t *info, void *context)
。 - 与
sa_handler
不同,sa_sigaction
提供了更多的信息,例如信号的额外数据(通过siginfo_t
结构),以及程序的上下文(通过ucontext_t
,通常作为void *
参数传递)。 - 如果设置了
SA_SIGINFO
标志,则应使用sa_sigaction
代替sa_handler
。
- 这是另一种信号处理函数的指针,类型为
- sa_mask:
- 类型为
sigset_t
,用于指定在当前信号处理函数执行期间需要阻塞的其他信号。 - 例如,如果你不希望在处理一个信号时被另一个信号中断,则可以将那个信号加入到 sa_mask 中。
- 类型为
- sa_flags:
- SA_RESTART:使被信号中断的系统调用自动重新启动。
- SA_NOCLDSTOP:如果设置了,子进程停止时不会通知父进程。
- SA_NOCLDWAIT:使父进程不等待子进程退出,防止僵尸进程。
- SA_SIGINFO:允许使用 sa_sigaction 字段而非 sa_handler。
- sa_restorer:不再使用。通常为NULL
2.2 方法介绍
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数:
signum
:信号编号act
:指向struct sigaction
结构的指针,该结构指定了新的信号处理设置oldact
:可选参数。如果不为NULL,则存储信号的前一个处理设置。
2.3 实例
#include <stdio.h>
#include <signal.h>
#include <string.h>
void handle_sigint(int sig, siginfo_t *info, void *context) {
printf("Received SIGINT (signal number %d).\n", sig);
printf("Signal originates from process %ld.\n", (long)info->si_pid);
}
int main() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handle_sigint;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &sa, NULL);
printf("Waiting for SIGINT (Ctrl+C)...\n");
while (1) {
sleep(1);
}
return 0;
}
三、signal()
signal()
允许程序指定一个信号处理函数,当特定信号到达时,该函数会被调用。其基本用法和功能比较简单,但在现代操作系统中,通常推荐使用更灵活且功能更强大的 sigaction()
代替。
不推荐使用。推荐使用sigaction()。
3.1 方法介绍
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
参数:
signum
:要处理的信号编号handler
:指向函数的指针,该函数将作为信号处理程序使用。这个参数还可以是特殊值 SIG_IGN(忽略信号)或 SIG_DFL(恢复信号的默认处理)。
3.2 实例
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
// 信号处理函数
void signal_handler(int signum) {
printf("Caught signal %d, but we're not exiting...\n", signum);
}
int main() {
// 设置 SIGINT 的处理函数为 signal_handler
if (signal(SIGINT, signal_handler) == SIG_ERR) {
printf("Error: Unable to set signal handler.\n");
return 1;
}
// 循环,等待信号的到来
while(1) {
printf("Sleeping for 1 second...\n");
sleep(1);
}
return 0;
}