当前位置: 首页 > article >正文

Linux之信号集基础

目录

  • 前言
  • 一、信号集基础API浅析
    • 1.1 sigemptyset
    • 1.2 sigfillset
    • 1.3 sigaddset
    • 1.4 sigdelset
    • 1.5 signismember
    • 1.6 sigprocmask
    • 1.7 sigpending
    • 1.8 sigwait
  • 二、demo演练
    • 2.1 sigismember检查信号
    • 2.2 主线程pthread_sigmask阻塞后无法捕捉到特定信号
    • 2.3 主线程pthread_sigmask阻塞后sigwait()捕捉特定信号
    • 2.4 sigfillset添加所有信号、alarm发送信号、ctrl+c退出
    • 2.5 示例4进化:添加线程,volatile 从内存读取最新值
  • 三、信号集的应用场景
  • 四、信号集VS信号量


前言

在 Linux 中,信号集(Signal Set)是一种用于管理和选择信号的机制。信号集可以用来屏蔽、限制或对特定信号进行操作,是信号处理的一个重要组成部分。信号是Linux系统中用于进程间通信的一种机制,它允许一个进程通知另一个进程发生了某种事件。

信号集的作用和用途可以概括如下:

  • 信号屏蔽和处理:
    信号集允许进程暂时屏蔽(忽略)或处理一组信号。进程可以设置一个信号集,指定在执行某些操作时不希望被某些信号打扰。这对于控制程序的执行流程和避免因信号处理不当导致的竞态条件非常重要。

  • 原子操作:
    对信号集的操作是原子的,这意味着在设置或清除信号集时,不会有其他信号处理函数同时修改信号集,从而保证了操作的一致性和安全性。

  • 信号的阻塞和解除阻塞:
    信号集可以用来阻塞一组信号,使得这些信号暂时不会影响进程。当进程准备好处理这些信号时,可以解除阻塞,允许信号被传递给进程。

  • 信号的等待和检测:
    进程可以使用信号集等待一组信号中的任何一个发生。这允许进程在等待信号时,不必无限期地阻塞,而是可以指定一个信号集,当信号集中的任何一个信号到达时,进程可以被唤醒并处理该信号。

  • 提高程序的响应性和稳定性:
    通过合理使用信号集,程序可以更好地响应外部事件,同时避免因信号处理不当导致的程序崩溃或数据不一致。

  • 系统调用的配合使用:
    信号集常与一些系统调用(如sigprocmask、sigsuspend、sigwait等)配合使用,以实现信号的灵活管理。

  • 多线程程序中的信号处理:
    在多线程程序中,信号集可以用来同步线程间的信号处理,确保信号的适当传递和处理。

一、信号集基础API浅析

1.1 sigemptyset

sigemptyset是初始化set所指向的信号集,让其中所有的信号的对应的比特位清零,表示该信号集不包含任何有效信号。

#include <signal.h>
int sigemptyset(sigset_t *set);

1.2 sigfillset

sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系统支持的所有信号。

#include <signal.h>
int sigfillset(sigset_t *set);

1.3 sigaddset

sigaddset把某个特定信号加上

#include <signal.h>
int sigaddset(sigset_t *set, int signo);

1.4 sigdelset

sigdelset把某个特定信号去掉

#include <signal.h>
int sigdelset(sigset_t *set, int signo);

1.5 signismember

signismember是为了判断某个信号是否在该信号集中。

#include <signal.h>
int sigismember(const sigset_t *set, int signo);

1.6 sigprocmask

sigprocmask函数用于检查或修改当前进程的信号屏蔽字(signal mask)。信号屏蔽字决定了在屏蔽期间哪些信号会被阻塞,即暂时不会被处理。

语法

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
  • int how:操作标志,决定如何修改信号屏蔽字:
    • SIG_BLOCK:把 set 指向的信号集中的信号添加到当前信号屏蔽字中。
    • SIG_UNBLOCK:从当前信号屏蔽字中移除 set 指向的信号集中的信号。
    • SIG_SETMASK:用 set 指向的信号集替换当前信号屏蔽字。
  • const sigset_t *set:指向要修改的新信号集的指针。
  • sigset_t *oldset:如果不为 NULL,则存储之前的信号屏蔽字

返回值

成功时返回 0。
失败时返回 -1,并设置 errno 以指示错误类型。


1.7 sigpending

用于获取当前进程挂起(未决)的信号集。未决信号是在被阻塞后尚未处理的信号。

#include <signal.h>
int sigpending(sigset_t *set);

1.8 sigwait

用于同步等待信号的到来。它将指定的信号集中的信号之一移出队列并返回其编号。

#include <signal.h>
int sigwait(const sigset_t *set, int *sig);
  • const sigset_t *set:指向一个 sigset_t 类型的信号集变量,该信号集指定要等待的信号。
  • int *sig:指向一个整数变量,用于存储被捕获的信号编号。

二、demo演练

2.1 sigismember检查信号

#include <stdio.h>
#include <signal.h>

int main() {
    sigset_t set;

    // 初始化信号集为空
    if (sigemptyset(&set) == -1) {
        perror("sigemptyset");
        return 1;
    }

    // 检查信号集是否包含 SIGINT
    if (sigismember(&set, SIGINT)) {
        printf("SIGINT is in the set\n");
    } else {
        printf("SIGINT is not in the set\n");
    }

    return 0;
}

程序运行输出:

SIGINT is not in the set

2.2 主线程pthread_sigmask阻塞后无法捕捉到特定信号

#include <stdio.h> 
#include <stdlib.h>   
#include <signal.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h> 
#include <unistd.h>   


void handler(int s){
    printf("handler :%d\n" ,s);
}

int main(int argc, char**argv)
{
    signal(SIGINT , handler);
    sigset_t mask;
    sigaddset(&mask , SIGINT);
    pthread_sigmask(SIG_BLOCK,&mask, NULL); //阻塞 SIGINT 信号
    //用ctrl+\ 终止
    while(1)
        pause();
    return 0;
}

程序运行输出:卡在死循环里。
去掉pthread_sigmask该行后,ctrl+c,SIGINT 信号绑定的程序handler()会正常执行。

2.3 主线程pthread_sigmask阻塞后sigwait()捕捉特定信号

#include <stdio.h>     
#include <stdlib.h>   
#include <signal.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>  
#include <unistd.h>  
//gcc 别忘了 -lpthread
void handler(int s){
    printf("handler :%d\n" ,s);
}

int main(int argc, char**argv)
{
    signal(SIGINT , handler);
    sigset_t mask;
    sigaddset(&mask , SIGINT);
  pthread_sigmask(SIG_BLOCK,&mask, NULL);
    //用ctrl+\ 终止
   // while(1)pause();

     int sig = 0;
     while(1){
        if (sigwait(&mask,&sig)  != 0 ){
            perror("sigwait :");
            continue;
        }
        printf(" ! main got signal : %d\n" , sig);
    }
    return 0;
}

程序运行输出: 按下ctrl+c,主线程捕捉到信号

^C ! main got signal : 2
^C ! main got signal : 2
^C ! main got signal : 2
^C ! main got signal : 2
^C ! main got signal : 2
^C ! main got signal : 2
^C ! main got signal : 2
^C ! main got signal : 2
^C ! main got signal : 2
  • sigwait(&mask, &sig) 是一个阻塞操作,它将等待 mask 中的信号到来。尽管主线程设置了对 SIGINT 的屏蔽,但是 sigwait() 允许等待到这个信号。一旦 SIGINT 信号到达,由 Ctrl+C 发送,信号会被添加到信号队列,而 sigwait() 允许线程在收到信号时进行处理。此时不会调用 handler,而是直接将信号交给 sigwait(),并变成返回值。

2.4 sigfillset添加所有信号、alarm发送信号、ctrl+c退出

#include <stdio.h>     
#include <stdlib.h>   
#include <signal.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>  
#include <unistd.h>  

volatile sig_atomic_t goon = 1;

static void * t1(void *arg){
    sigset_t * mask = (sigset_t *)arg;
    int sig = 0;

    while(1){
        if ( sigwait(mask,&sig)  != 0 ){
            perror(" thread sigwait :");
            continue;
        }
        printf("thread got signal : %d\n" , sig);
        if(SIGINT == sig){
            goon = 0;
            break;
        }
    }
}
int main(int argc, char**argv)
{
    sigset_t mask;
    sigfillset(&mask); //添加所有信号
    pthread_sigmask(SIG_BLOCK,&mask, NULL);
    pthread_t t;
    pthread_create(&t,0,t1,&mask);

    int sig = 0;
    while(goon){
        alarm(1);
        sleep(1);
    }

    pthread_join(t,0);
    puts("end");
    return 0;
}

程序运行输出: 每秒发送对应信号并捕获输出,按下ctrl+c,进程退出

thread got signal : 14
thread got signal : 14
thread got signal : 14
thread got signal : 14
thread got signal : 14
thread got signal : 14
^Cthread got signal : 2
end

2.5 示例4进化:添加线程,volatile 从内存读取最新值

#include <stdio.h>     
#include <stdlib.h>   
#include <signal.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>  
#include <unistd.h>  
#include <errno.h>


volatile sig_atomic_t goon = 1;

static void * worker (void *arg){
    while(goon){
        printf("worker is running , tid:%ld\n" , pthread_self());
        sleep(5);
    }
    puts("worker is done");
}

static void * sig_handler_thread(void *arg){
    sigset_t * mask = (sigset_t *)arg;
    int sig = 0;
    pthread_t tid = pthread_self();
    while(1){
        if ( sigwait(mask,&sig)  != 0 ){
            printf("sigwait error : %s\n" , strerror(errno));
            continue;
        }
        printf("thread :%ld got signal : %d\n" , tid,sig);
        if(SIGINT == sig){
            goon = 0;
            break;
        }
    }
}

int main(int argc, char**argv)
{
    sigset_t mask;
    sigfillset(&mask);
    pthread_sigmask(SIG_BLOCK,&mask, NULL);
    pthread_t tid1, tid2;
    pthread_create(&tid1,0,sig_handler_thread,&mask);
    pthread_create(&tid2,0,worker,NULL);

    int sig = 0;
    while(goon){
        alarm(1);
        sleep(1);
    }

    pthread_join(tid2,0);
    pthread_join(tid1, NULL);
    puts("end");
    return 0;
}

程序运行输出: 与上例类似,自行理解。

worker is running , tid:139906703542016
thread :139906711934720 got signal : 14
thread :139906711934720 got signal : 14
thread :139906711934720 got signal : 14
thread :139906711934720 got signal : 14
worker is running , tid:139906703542016
thread :139906711934720 got signal : 14
thread :139906711934720 got signal : 14
thread :139906711934720 got signal : 14
thread :139906711934720 got signal : 14
thread :139906711934720 got signal : 14
worker is running , tid:139906703542016
thread :139906711934720 got signal : 14
^Cthread :139906711934720 got signal : 2
worker is done
end

三、信号集的应用场景

定义: 信号集是一种用于管理多个信号的机制,允许进程或线程一起阻塞或等待多个信号。

常用场景:

  • 信号处理:
    在需要处理特定信号时,可以使用信号集来定义要阻塞的信号。例如,在多线程程序中,可以屏蔽某些信号,使得信号在关键代码执行期间不会中断,从而避免状态不一致。

  • 线程同步:
    当多个线程需要等待特定事件(例如消息到达或信号触发)时,可以使用 sigwait() 等函数来实现。这样可以避免在特定上下文中出现信号直接调用处理程序的问题,也能够更好地管理线程状态。

  • 状态监控:
    信号集可以用来监控系统状态变化。例如,在处理 Unix/Linux 系统的守护进程时,可以使用信号集进行状态更新和管理,从而处理后台服务的停启和运行状态。

  • 多线程信号传递:
    在多线程应用中,使用信号集可以协调多个线程之间的信号传递,确保在运行时能够安全的捕获并处理来自外部的信号,如终止信号或定时信号。


四、信号集VS信号量

信号量: 主要用于控制对共享资源的访问,帮助管理并发操作,适合资源限制和冲突的问题。
信号集: 主要用于信号的处理和管理,帮助确保在多线程和多进程环境中有效捕获和处理信号。


http://www.kler.cn/a/422049.html

相关文章:

  • 使用PaddlePaddle实现线性回归模型
  • 【机器学习】窥数据之序,悟算法之道:机器学习的初心与远方
  • k8s使用的nfs作为sc。
  • 【C++】数组
  • 【QNX+Android虚拟化方案】132 - QNX 系统内存、CPU负载监控
  • brew安装mongodb和php-mongodb扩展新手教程
  • 怎么自己创建一个网站? 开发语言首选 java,使用CMS网站内容管理系统是不错的选择
  • Redis设计与实现第17章 -- 集群 总结2(执行命令 重新分片)
  • 能源投资工程VS智能驾驶,DolphinScheduler如何当好传统行业与前沿科技的桥梁?
  • [网络] UDP通信接口及一些简易项目
  • vue 通过 mqtt 实现实时接收消息
  • 算法笔记:力扣24. 两两交换链表中的节点
  • centos7下安装promethus及grafana
  • flutter 报错 error: unable to find git in your path.
  • MongoDB注入攻击测试与防御技术深度解析
  • 若依前端问题
  • redis针对string的命令及应用场景
  • API 数据接口使用与安全指南
  • vitess使用记录:vtctldclient,设置分表规则
  • Postgres 如何使事务原子化?
  • [每周一更]-(第125期):模拟面试|NoSQL面试思路解析
  • 备赛蓝桥杯--算法题目(2)
  • 基于Matlab地形和环境因素的森林火灾蔓延模拟与可视化研究
  • Windows系统搭建Docker
  • 040集——CAD中放烟花(CAD—C#二次开发入门)
  • qt6 oob