掌握Linux信号集操作技巧
在 Linux 系统编程中,信号集(signal set) 是一组信号的集合,用于管理进程对信号的响应。信号集操作函数(如 sigemptyset
、sigfillset
、sigaddset
等)允许程序员初始化、填充、添加、删除和检查信号集,从而实现对信号的精细控制。
目录
一、信号集数据类型 sigset_t
使用规则:
二、信号集操作函数
1. sigemptyset - 清空信号集
示例代码:
2. sigfillset - 填充信号集
示例代码:
3. sigaddset - 向信号集中添加信号
示例代码:
4. sigdelset - 从信号集中删除信号
示例代码:
5. sigismember - 检查信号是否在信号集中
示例代码:
三、信号集与 sigprocmask 的结合使用
sigprocmask 函数原型:
示例代码:阻塞和解除阻塞信号
四、总结知识点图解(知识树状图)
五、课后练习建议
一、信号集数据类型 sigset_t
sigset_t
是 Linux 中用于表示信号集的数据类型。它本质上是一个位掩码(bitmask),每个位对应一个信号。例如,第 2 位对应 SIGINT
(信号编号 2),第 9 位对应 SIGKILL
(信号编号 9)。
使用规则:
- 必须初始化:在使用
sigset_t
之前,必须调用sigemptyset
或sigfillset
初始化。 - 避免直接操作:不能直接对
sigset_t
进行位操作,必须通过标准函数操作。
二、信号集操作函数
1. sigemptyset
- 清空信号集
#include <signal.h>
int sigemptyset(sigset_t *set);
- 功能:将信号集
set
初始化为空集(所有位清零)。 - 返回值:成功返回 0,失败返回 -1(通常不会失败)。
示例代码:
#include <signal.h>
#include <stdio.h>int main() {sigset_t set;if (sigemptyset(&set) == -1) {perror("sigemptyset");return 1;}printf("Signal set is empty.\n");return 0;
}
2. sigfillset
- 填充信号集
#include <signal.h>
int sigfillset(sigset_t *set);
- 功能:将信号集
set
初始化为包含所有信号(所有位置 1)。 - 返回值:成功返回 0,失败返回 -1。
示例代码:
#include <signal.h>
#include <stdio.h>int main() {sigset_t set;if (sigfillset(&set) == -1) {perror("sigfillset");return 1;}printf("All signals are added to the set.\n");return 0;
}
3. sigaddset
- 向信号集中添加信号
#include <signal.h>
int sigaddset(sigset_t *set, int signum);
- 功能:将信号
signum
添加到信号集set
中。 - 返回值:成功返回 0,失败返回 -1(如信号编号无效)。
示例代码:
#include <signal.h>
#include <stdio.h>int main() {sigset_t set;if (sigemptyset(&set) == -1) {perror("sigemptyset");return 1;}if (sigaddset(&set, SIGINT) == -1) {perror("sigaddset");return 1;}printf("SIGINT added to the signal set.\n");return 0;
}
4. sigdelset
- 从信号集中删除信号
#include <signal.h>
int sigdelset(sigset_t *set, int signum);
- 功能:将信号
signum
从信号集set
中删除。 - 返回值:成功返回 0,失败返回 -1(如信号编号无效)。
示例代码:
#include <signal.h>
#include <stdio.h>int main() {sigset_t set;if (sigfillset(&set) == -1) {perror("sigfillset");return 1;}if (sigdelset(&set, SIGINT) == -1) {perror("sigdelset");return 1;}printf("SIGINT removed from the signal set.\n");return 0;
}
5. sigismember
- 检查信号是否在信号集中
#include <signal.h>
int sigismember(const sigset_t *set, int signum);
- 功能:检查信号
signum
是否在信号集set
中。 - 返回值:
1
:信号在集合中0
:信号不在集合中-1
:失败(如集合未初始化)
示例代码:
#include <signal.h>
#include <stdio.h>int main() {sigset_t set;if (sigemptyset(&set) == -1) {perror("sigemptyset");return 1;}if (sigaddset(&set, SIGINT) == -1) {perror("sigaddset");return 1;}if (sigismember(&set, SIGINT)) {printf("SIGINT is in the set.\n");} else {printf("SIGINT is not in the set.\n");}return 0;
}
三、信号集与 sigprocmask
的结合使用
信号集的一个核心用途是通过 sigprocmask
函数修改进程的信号屏蔽字(blocked signal mask),从而控制哪些信号被阻塞或允许传递。
sigprocmask
函数原型:
#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:输入的信号集
- oldset:保存旧的信号屏蔽字
示例代码:阻塞和解除阻塞信号
#include <signal.h>
#include <stdio.h>
#include <unistd.h>void handle_sigint(int signo) {printf("Caught SIGINT\n");
}int main() {struct sigaction sa;sigset_t new_mask, old_mask;// 设置 SIGINT 处理函数sa.sa_handler = handle_sigint;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction");return 1;}// 阻塞 SIGINTsigemptyset(&new_mask);sigaddset(&new_mask, SIGINT);if (sigprocmask(SIG_BLOCK, &new_mask, &old_mask) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is blocked. Press Ctrl+C...\n");sleep(5); // 在这期间按 Ctrl+C 不会触发处理函数// 解除阻塞if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is unblocked. Press Ctrl+C again...\n");pause(); // 现在按 Ctrl+C 会触发处理函数return 0;
}
四、总结知识点图解(知识树状图)
信号集操作(sigemptyset, sigfillset, sigaddset等)
│
├── sigset_t 类型
│ ├── 位掩码表示信号集合
│ └── 必须初始化后才能使用
│
├── sigemptyset()
│ ├── 功能:清空信号集
│ └── 示例:初始化空集
│
├── sigfillset()
│ ├── 功能:填充所有信号
│ └── 示例:包含所有信号
│
├── sigaddset()
│ ├── 功能:添加指定信号
│ └── 示例:添加 SIGINT
│
├── sigdelset()
│ ├── 功能:删除指定信号
│ └── 示例:删除 SIGINT
│
├── sigismember()
│ ├── 功能:检查信号是否在集合中
│ └── 示例:判断 SIGINT 是否存在
│
└── sigprocmask()├── 功能:修改进程的信号屏蔽字├── how 参数:SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK└── 示例:阻塞和解除阻塞信号
五、课后练习建议
-
编写程序验证信号集操作函数的行为
- 使用
sigismember
检查不同信号是否在集合理论。 - 尝试添加和删除多个信号,观察结果。
- 使用
-
实现信号屏蔽与解除屏蔽
- 使用
sigprocmask
阻塞SIGINT
和SIGTERM
,然后解除阻塞。 - 在解除阻塞后测试信号是否能被正常捕获。
- 使用
-
结合
sigaction
和信号集实现复杂逻辑- 在信号处理函数中临时阻塞其他信号,避免竞态条件。
-
探索错误处理
- 在信号集操作失败时(如无效信号编号),使用
perror
或strerror(errno)
分析原因。
- 在信号集操作失败时(如无效信号编号),使用
-
多线程环境下的信号集操作
- 使用
pthread_sigmask
替代sigprocmask
,了解线程级的信号屏蔽。
- 使用
gcc your_signal_set_program.c -o your_signal_set_program
./your_signal_set_program
kill -<signal_number> <PID>