Linux信号集操作函数详解

Linux信号集操作函数详解

  • [1. 信号集概述](#1. 信号集概述)
  • [2. 信号集的基本操作函数](#2. 信号集的基本操作函数)
    • [2.1 初始化和清空信号集](#2.1 初始化和清空信号集)
    • [2.2 进程信号掩码操作](#2.2 进程信号掩码操作)
    • [2.3 检查挂起的信号](#2.3 检查挂起的信号)
    • [2.4 等待信号](#2.4 等待信号)
  • [3. 高级信号集操作](#3. 高级信号集操作)
    • [3.1 信号集与字符串转换](#3.1 信号集与字符串转换)
    • [3.2 多线程环境中的信号处理](#3.2 多线程环境中的信号处理)
  • [4. 实际应用案例](#4. 实际应用案例)
    • [4.1 安全地处理临界区](#4.1 安全地处理临界区)
    • [4.2 等待特定信号](#4.2 等待特定信号)
  • [5. 注意事项](#5. 注意事项)
  • [6. 总结](#6. 总结)

1. 信号集概述

在Linux系统中,信号集(sigset_t)是用来表示一组信号的数据结构。它本质上是一个位掩码,每一位对应一个特定的信号。信号集操作函数允许我们方便地管理多个信号,常用于信号屏蔽、信号等待等场景。

信号集的主要用途包括:

  • 设置进程的信号掩码(阻塞/非阻塞特定信号)
  • 检查挂起的信号
  • 等待特定信号的发生

2. 信号集的基本操作函数

2.1 初始化和清空信号集

c 复制代码
#include <signal.h>

int sigemptyset(sigset_t *set);      // 清空信号集,所有信号位设为0
int sigfillset(sigset_t *set);       // 填充信号集,所有信号位设为1
int sigaddset(sigset_t *set, int signo);   // 向信号集中添加信号
int sigdelset(sigset_t *set, int signo);   // 从信号集中删除信号
int sigismember(const sigset_t *set, int signo); // 检查信号是否在信号集中

示例代码:

c 复制代码
sigset_t mask;
sigemptyset(&mask);      // 初始化空信号集
sigaddset(&mask, SIGINT); // 添加SIGINT信号
sigaddset(&mask, SIGQUIT); // 添加SIGQUIT信号

if (sigismember(&mask, SIGINT)) {
    printf("SIGINT is in the signal set\n");
}

2.2 进程信号掩码操作

c 复制代码
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

how参数指定操作方式:

  • SIG_BLOCK:将set中的信号添加到当前阻塞信号集中
  • SIG_UNBLOCK:从当前阻塞信号集中移除set中的信号
  • SIG_SETMASK:将当前阻塞信号集设置为set

使用示例:

c 复制代码
sigset_t new_mask, old_mask;

sigemptyset(&new_mask);
sigaddset(&new_mask, SIGINT);

// 阻塞SIGINT信号,并保存旧的信号掩码
if (sigprocmask(SIG_BLOCK, &new_mask, &old_mask) == -1) {
    perror("sigprocmask");
    exit(EXIT_FAILURE);
}

// 临界区代码,不会被SIGINT中断

// 恢复旧的信号掩码
if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
    perror("sigprocmask");
    exit(EXIT_FAILURE);
}

2.3 检查挂起的信号

c 复制代码
int sigpending(sigset_t *set);

该函数获取当前被阻塞且处于挂起状态的信号集。

示例:

c 复制代码
sigset_t pending;

if (sigpending(&pending) == -1) {
    perror("sigpending");
    exit(EXIT_FAILURE);
}

if (sigismember(&pending, SIGINT)) {
    printf("SIGINT is pending\n");
}

2.4 等待信号

c 复制代码
int sigsuspend(const sigset_t *mask);

该函数临时将进程的信号掩码设置为mask,然后挂起进程直到收到一个信号。信号处理函数执行完毕后,sigsuspend返回,并恢复原来的信号掩码。

典型用法:

c 复制代码
sigset_t empty_mask;
sigemptyset(&empty_mask);

// 原子操作:解除信号阻塞并等待
sigsuspend(&empty_mask);

3. 高级信号集操作

3.1 信号集与字符串转换

c 复制代码
#include <string.h>

int sigsetfromstr(sigset_t *set, const char *str);
int sigsettostr(const sigset_t *set, char *str, size_t maxlen);

这些函数(非标准)可以方便地在信号集和字符串表示之间转换。

3.2 多线程环境中的信号处理

在多线程程序中,应使用pthread_sigmask而不是sigprocmask

c 复制代码
#include <pthread.h>

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);

用法与sigprocmask类似,但作用范围是调用线程而非整个进程。

4. 实际应用案例

4.1 安全地处理临界区

c 复制代码
void critical_section(void)
{
    sigset_t new_mask, old_mask;
    
    // 阻塞SIGINT和SIGTERM
    sigemptyset(&new_mask);
    sigaddset(&new_mask, SIGINT);
    sigaddset(&new_mask, SIGTERM);
    
    if (pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask) != 0) {
        perror("pthread_sigmask");
        return;
    }
    
    // 执行临界区代码
    // ...
    
    // 恢复信号掩码
    if (pthread_sigmask(SIG_SETMASK, &old_mask, NULL) != 0) {
        perror("pthread_sigmask");
    }
}

4.2 等待特定信号

c 复制代码
void wait_for_signal(void)
{
    sigset_t wait_mask;
    int sig;
    
    sigemptyset(&wait_mask);
    sigaddset(&wait_mask, SIGUSR1);
    sigaddset(&wait_mask, SIGUSR2);
    
    printf("Waiting for SIGUSR1 or SIGUSR2...\n");
    
    // 阻塞所有信号除了wait_mask中的信号
    sigprocmask(SIG_BLOCK, &wait_mask, NULL);
    
    // 等待信号
    sigwait(&wait_mask, &sig);
    
    printf("Received signal %d\n", sig);
}

5. 注意事项

  1. 信号编号范围 :不同系统可能有不同的信号编号范围,使用前应检查<signal.h>中的定义。

  2. 实时信号:对于实时信号(SIGRTMIN到SIGRTMAX),信号集操作同样适用。

  3. 可重入性:信号处理函数中应避免使用非可重入函数,如printf、malloc等。

  4. 原子性sigsuspend提供了原子性的"解除阻塞+等待"操作,比单独调用sigprocmaskpause更安全。

  5. 多线程:在多线程程序中,信号处理是进程范围的,但信号掩码是线程范围的。

6. 总结

Linux信号集操作函数为信号处理提供了灵活而强大的工具集。通过合理使用这些函数,可以实现:

  • 精确控制哪些信号会被阻塞
  • 安全地处理临界区代码
  • 高效地等待特定信号
  • 在多线程环境中正确管理信号

掌握这些函数对于编写健壮的、可响应信号的Linux应用程序至关重要。在实际开发中,应根据具体需求选择合适的函数组合,并注意信号处理的原子性和线程安全性问题。

相关推荐
咨询QQ688238861 小时前
开关磁阻电机控制仿真:Matlab 2016b的探索之旅
c++
eggrall1 小时前
Linux 基础开发工具 —— 解锁高效开发的底层密钥
linux·运维·服务器
落霞的思绪1 小时前
基于Go开发的矢量瓦片服务器——pg_tileserv
开发语言·后端·golang
喜欢吃燃面1 小时前
算法竞赛之排序算法
c++·学习·算法
CHANG_THE_WORLD1 小时前
Python 文件操作详解与代码示例
开发语言·数据库·python
哇哈哈&1 小时前
rabbitmq最简单的安装方法
linux·运维·centos
虎头金猫1 小时前
从杂乱到有序,Paperless-ngx 加个cpolar更好用
linux·运维·人工智能·docker·开源·beautifulsoup·pandas
大大da怪i1 小时前
GPU编程之warp级编程
c++·gpu算力
我发在否1 小时前
C++ > 牛客OJ在线编程常见输入输出练习场
c++·acm·牛客