Linux信号sigaction / signal

news2024/10/7 6:44:13

Linux信号sigaction / signal

文章目录

  • Linux信号sigaction / signal
    • 目的
    • 函数原型
    • struct sigaction
    • 信号枚举值
      • ISO C99 signals.
      • Historical signals specified by POSIX.
      • New(er) POSIX signals (1003.1-2008, 1003.1-2013).
      • Nonstandard signals found in all modern POSIX systems(including both BSD and Linux).
    • 测试
      • 1 处理信号后重启系统调用
      • 2 处理信号后不重启系统调用
      • 3 使用 sigaction 函数为 SIGUSR1 和 SIGUSR2 信号注册处理函数
    • 实际代码框架
      • 1. signal
      • 2 将程序放入线程中
    • 终端间通过kill发送信号
    • Reference

目的

我们可以通过sigaction函数或者signal指定某种特定信号,在程序执行过程中,通过发送信号,达到改变程序运行状态的目的,比如让程序停止等。

函数原型

获取或者设定与指定信号相关联的处理动作。

/* Get and/or set the action for signal SIG.  */
extern int sigaction (int __sig, const struct sigaction *__restrict __act,
		      struct sigaction *__restrict __oact) __THROW;
		      
extern __sighandler_t signal (int __sig, __sighandler_t __handler)
     __THROW;

sigaction函数执行信号处理程序时,会把当前信号加入到进程的信号屏蔽字中,从而防止在进行信号处理期间信号丢失。

程序接收信号示意图

对signal(),Linux默认会自动重启动被中断的系统调用;

而对于 sigaction(),Linux默认并不会自动重启动,所以如果希望执行信号处理后自动重启动先前中断的系统调用,就需要为sa_flags指定 SA_RESTART标志

struct sigaction

当信号到达时,用于描述采取的动作的结构

/* Structure describing the action to be taken when a signal arrives.  */
struct sigaction
  {
    /* Signal handler.  */
#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED
    union
      {
	/* Used if SA_SIGINFO is not set.  */
	__sighandler_t sa_handler;
	/* Used if SA_SIGINFO is set.  */
	void (*sa_sigaction) (int, siginfo_t *, void *);
      }
    __sigaction_handler;
# define sa_handler	__sigaction_handler.sa_handler
# define sa_sigaction	__sigaction_handler.sa_sigaction
#else
    __sighandler_t sa_handler;
#endif

    /* Additional set of signals to be blocked.  */
    __sigset_t sa_mask;

    /* Special flags.  */
    int sa_flags;

    /* Restore handler.  */
    void (*sa_restorer) (void);
  };
  • sa_handler 和signal()的参数__handler相同,代表新的信号处理函数
  • sa_mask 表示处理信号时,需要阻止的信号集
  • sa_flags 处理信号时的标志位
    • SA_RESETHAND:在调用处理函数前,将信号的处理函数重置为缺省值(即SIG_IGN)
    • SA_RESTART:执行信号处理后,自动重启先前中断的系统调用
    • SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
    • SA_ONSTACK:使用由sigaltstack()安装的备选栈
    • SA_SIGINFO:调用信号处理程序时携带了额外参数(提供了关于信号的深入信息)

信号枚举值

ISO C99 signals.

符号数值含义
SIGINT2Interactive attention signal.
SIGILL4Illegal instruction.
SIGABRT6Abnormal termination.
SIGFPE8Erroneous arithmetic operation.
SIGSEGV11Invalid access to storage.
SIGTERM15Termination request.

Historical signals specified by POSIX.

符号数值含义
SIGHUP1Hangup.
SIGQUIT3Quit.
SIGTRAP5Trace/breakpoint trap.
SIGKILL9Killed.
SIGBUS10Bus error.
SIGSYS12Bad system call.
SIGPIPE13Broken pipe.
SIGALRM14Alarm clock.

New(er) POSIX signals (1003.1-2008, 1003.1-2013).

符号数值含义
SIGURG16Urgent data is available at a socket.
SIGSTOP17Stop, unblockable.
SIGTSTP18Keyboard stop.
SIGCONT19Continue.
SIGCHLD20Child terminated or stopped.
SIGTTIN21Background read from control terminal.
SIGTTOU22Background write to control terminal.
SIGPOLL23Pollable event occurred (System V).
SIGXCPU24CPU time limit exceeded.
SIGXFSZ25File size limit exceeded.
SIGVTALRM26Virtual timer expired.
SIGPROF27Profiling timer expired.
SIGUSR130User-defined signal 1.
SIGUSR231User-defined signal 2.

Nonstandard signals found in all modern POSIX systems(including both BSD and Linux).

符号数值含义
SIGWINCH28Window size change (4.3 BSD, Sun).

测试

1 处理信号后重启系统调用

#include <iostream>
#include "stdio.h"
#include <signal.h>
#include <stdlib.h>

void signalHandle(int signum) {
	if(signum  = SIGINT) {
        std::cout << "SIGINT recived" << std::endl;
	}
}

int main(void){
	struct sigaction act;

	sigemptyset(&act.sa_mask);
	//这里使用SA_RESTART执行信号处理后自动重启到先前中断的系统调用,可以多次捕捉信号
	act.sa_flags = (SA_SIGINFO|SA_RESTART);
	act.sa_handler = signalHandle;
	sigaction(SIGINT, &act, NULL);

	while(1){

    }
}

当按下 CTRL + c时,会一直打印。

$ ./main 
^CSIGINT recived
^CSIGINT recived
^CSIGINT recived

2 处理信号后不重启系统调用

#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

void show_handler(int sig)
{
    printf("I got signal %dn\n", sig);
    int i;
    for(i = 0; i < 5; i++) 
   {
        printf("i = %dn\n", i);
        sleep(1);
    }
}

int main(void)
{
    // section 1
    int i = 0;
    struct sigaction act, oldact;
    act.sa_handler = show_handler;
    sigaddset(&act.sa_mask, SIGQUIT);         
    act.sa_flags = SA_RESETHAND | SA_NODEFER; 
    //act.sa_flags = 0;                       
 
    sigaction(SIGINT, &act, &oldact);
    while(1) 
   {
        sleep(1);
        printf("sleeping %dn\n", i);
        i++;
    }
    return 0;
}

程序起来后,控制台输入CTRL + c,能够接收到信号。再次输入时,程序退出。

(base) qiancj@qiancj-Dell-G15-5510:~/codes/test/build$ ./main 
sleeping 0
sleeping 1
^CI got signal 2
i = 0
i = 1
i = 2
i = 3
i = 4
sleeping 2
^C
(base) qiancj@qiancj-Dell-G15-5510:~/codes/test/build$ 

3 使用 sigaction 函数为 SIGUSR1 和 SIGUSR2 信号注册处理函数

#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

static void sig_usr(int signum)
{
    if(signum == SIGUSR1)
    {
        printf("SIGUSR1 received\n");
    }
    else if(signum == SIGUSR2)
    {
        printf("SIGUSR2 received\n");
    }
    else
    {
        printf("signal %d received\n", signum);
    }
}


int main(void)
{
// section 2
   char buf[512];
    int  n;
    struct sigaction sa_usr;
    sa_usr.sa_flags = 0;
    sa_usr.sa_handler = sig_usr;   //信号处理函数
    
    sigaction(SIGUSR1, &sa_usr, NULL);
    sigaction(SIGUSR2, &sa_usr, NULL);
    
    printf("My PID is %dn\n", getpid());
    
    while(1)
    {
        if((n = read(STDIN_FILENO, buf, 511)) == -1)
        {
            if(errno == EINTR)
            {
                printf("read is interrupted by signal\n");
            }
        }
        else
        {
            buf[n] = '0';
            printf("%d bytes read: %s\n", n, buf);
        }
    }
    return 0;
}

当一个终端启动程序,获取当前PID号(13770),另一个终端输入kill -USR1 32230或者kill -USR2 32230,第一个终端就会收到中断信号

(base) qiancj@qiancj-Dell-G15-5510:~/codes/test/build$ ./main
My PID is 32230n

SIGUSR1 received
read is interrupted by signal
SIGUSR2 received
read is interrupted by signal

实际代码框架

当程序需要一直运行时,需要人为中断停止时,可以使用信号函数,具体框架可以设置为以下2种:

1. signal

#include <csignal>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>

sig_atomic_t stop_flag = 0;

void INTSigHandler(int32_t num) {
  stop_flag = 1;
  std::cout << "  Signal Interactive attention " << num << " received."
            << std::endl;
}

int main(int argc, char **argv) {
  signal(SIGINT, INTSigHandler);

  // work always....
  ...
   
  while ((!stop_flag)) {
    LOG_INFO << "I am working...";
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  // Stop working! Go to rest!

  return 0;
}

2 将程序放入线程中

#include <chrono>
#include <csignal>
#include <cstdint>
#include <cstdio>
#include <cstdlib>

namespace {

std::atomic_bool continueExecution{true};

void SigTermHandler(int signal) {
  if (signal == SIGTERM || signal == SIGINT) {
    // set atomic exit flag
    continueExecution = false;
  }
}

bool RegisterSigTermHandler() {
  struct sigaction sa;
  sa.sa_handler = SigTermHandler;
  sa.sa_flags = 0;
  sigemptyset(&sa.sa_mask);
  // register signal handler
  if (sigaction(SIGTERM, &sa, NULL) == -1) {
    // Could not register a SIGTERM signal handler
    return false;
  }

  if (sigaction(SIGINT, &sa, NULL) == -1) {
    // Could not register a SIGTERM signal handler
    return false;
  }
  return true;
}

}  // namespace

void ThreadAct1();

void ThreadAct1() {
  while (continueExecution) {
    // always work...
    ...
    // sleep
    std::this_thread::sleep_for(std::chrono::milliseconds(10000));
  }
}

int main(int argc, char *argv[]) {

  if (!RegisterSigTermHandler()) {
    std::cout << "Unable to register signal handler" << std::endl;
  }

  // start thread
  std::thread act1(ThreadAct1);
  act1.join();

  return 0;
}

终端间通过kill发送信号

(base) qiancj@qiancj-Dell-G15-5510:~$ kill --help
kill: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
    Send a signal to a job.
    
    Send the processes identified by PID or JOBSPEC the signal named by
    SIGSPEC or SIGNUM.  If neither SIGSPEC nor SIGNUM is present, then
    SIGTERM is assumed.
    
    Options:
      -s sig	SIG is a signal name
      -n sig	SIG is a signal number
      -l	list the signal names; if arguments follow `-l' they are
    		assumed to be signal numbers for which names should be listed
      -L	synonym for -l
    
    Kill is a shell builtin for two reasons: it allows job IDs to be used
    instead of process IDs, and allows processes to be killed if the limit
    on processes that you can create is reached.
    
    Exit Status:
    Returns success unless an invalid option is given or an error occurs.

信号种类

(base) qiancj@qiancj-Dell-G15-5510:~$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

Reference

  • linux中sigaction函数详解
  • sigaction详细解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/412770.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虹科教您 | 基于Linux系统的RELY-TSN-KIT套件操作指南(1)——硬件设备与操作环境搭建

RELY-TSN-KIT是一款针对TSN的开箱即用的解决方案&#xff0c;它可以无缝实施确定性以太网网络&#xff0c;并从这些技术复杂性中抽象出用户设备和应用。该套件可评估基于IEEE 802.1AS同步的时间常识的重要性&#xff0c;并借助时间感知整形器来确定性地交付实时流量&#xff0c…

判断完全二叉树(层序遍历)| C

层序遍历 基本思路&#xff1a;利用队列&#xff0c;出上一层&#xff0c;带下一层&#xff08;NULL不入队列&#xff09; &#xff08;C语言需要自己构建队列→【队列】&#xff1c;用链表实现队列&#xff1e; | [数据结构] | C语言&#xff09; 代码 #include "Queu…

代码自动发布系统

之前是jenkins发现gitlab代码更新了就自动获取直接部署到服务器 现在是jenkins自动获取Code之后打包成镜像上传到仓库然后通知docker去拉取更新的镜像 分析 旧∶ 代码发布环境提前准备&#xff0c;以主机为颗粒度静态 新: 代码发布环境多套&#xff0c;以容器为颗粒度编译 …

Typora设置修改字体颜色快捷键

目录 1.typora如何设置修改字体颜色快捷键 2. AutoHotKey软件安装 3.typora关于AutoHotKey的具体操作 1.typora如何设置修改字体颜色快捷键 typora本身是不能直接修改字体颜色的&#xff0c;不过若是想修改还是可以用一些代码去改变的&#xff0c;但是每次都修改一次实在麻烦…

mysql常用的基础命令

通过学习mysql命令提高数据处理和工作效率 基础命令 1.登录MySQL mysql -u root -p 2.查看当前系统所有数据库 show databases; 3.切换数据库 use 数据库名称 4.查看数据库下的所有表 show tables; 5.查看表结构&#xff1b; desc 表名&#xff1b; 6.创建数据库 crea…

MAC OS(M1)安装配置miniconda

一、下载安装miniconda miniconde官网&#xff1a;Miniconda — Conda documentation M1最低只能适配到python3.8 打开终端,进入安装包所在文件夹&#xff0c;使用命令进行安装 bash Miniconda3-latest-MacOSX-arm64.sh一路回车 二、配置环境 安装完成后重启终端&#xf…

Unity ads广告插件的使用

介绍 Unity Ads SDK 由领先的移动游戏引擎创建,无论您在 Unity、Xcode 还是 Android Studio 中进行开发,都能为您的游戏提供全面的货币化框架。 使用 Unity Ads 将各种广告格式合并到游戏中的自然呈现点中。例如,您可以实施激励视频广告来构建更强大的游戏经济,同时为您的…

[C++笔记]vector

vector vector的说明文档 vector是表示可变大小数组的序列容器(动态顺序表)。就像数组一样&#xff0c;vector也采用连续的存储空间来储存元素。这就意味着可以用下标对vector的元素进行访问&#xff0c;和数组一样高效。与数组不同的是&#xff0c;它的大小可以动态改变——…

1700页,卷S人的 Java《八股文》PDF手册,涨薪跳槽拿高薪就靠它了

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;又得准备面试了&#xff0c;不知道从何下手&#xff01; 不论是跳槽涨薪&#xff0c;还是学习提升&#xff01;先给自己定一个小目标&#xff0c;然后再朝着目标去努力就完事儿了&#xff01; 为了帮大家节约时间&a…

Mybatis一级缓存和二级缓存(带测试方法)

目录 一、什么是缓存 二、Mabtis一级缓存 &#xff08;1&#xff09;测试一级缓存 &#xff08;2&#xff09;清空一级缓存 三、Mybatis二级缓存 &#xff08;1&#xff09;开启二级缓存 &#xff08;2&#xff09;测试二级缓存 一、什么是缓存 缓存是内存当中一块存储数…

蓝桥杯嵌入式第十一届省赛题目解析

写完第十一届蓝桥杯嵌入式省赛题目&#xff0c;拿出来给大家参考参考&#xff0c;也是让大家一起测试看看有什么问题还需要改进&#xff0c;代码在最后喔。 目录 客观题&#xff1a; 程序设计题 &#xff1a; 题目解析&#xff1a; CubeMX配置 代码演示 &#xff1a; 客观…

Windows环境下实现设计模式——职责链模式(JAVA版)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows环境下如何编程实现职责链模式&#xff08;设计模式&#xff09;。 不知道大家有没有这样的感觉&#xff0c;看了一大堆编程和设计模式的书&#xff0c;却还是很难理解设计模式&#xff…

spring boot Websocket(使用笔记)

使用websocket有两种方式&#xff1a;1是使用sockjs&#xff0c;2是使用h5的标准。使用Html5标准自然更方便简单&#xff0c;所以记录的是配合h5的使用方法。 1、pom 核心是ServerEndpoint这个注解。这个注解是Javaee标准里的注解&#xff0c;tomcat7以上已经对其进行了实现&a…

学内核之十八:纸上得来终觉浅,绝知此事要躬行

目录 0 前言 1 ioremap、vmalloc与原子上下文 2 copy_to_user与进程上下文 3 fasync与指针初始化 4 wait_event_interruptible与条件变量 0 前言 大家都知道&#xff0c;内核开发跟应用开发&#xff0c;体验是完全不同的&#xff0c;尤其是驱动。一方面要掌握扎实的语言基…

MySQL 基本轮廓

目录 什么是数据库 主流数据库 基本使用 连接服务器 服务器管理 使用案例 创建数据库 使用数据库 创建数据库表 表中插入数据 查询表中的数据 服务器&#xff0c;数据库&#xff0c;表关系 MySQL架构 什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄…

每日一问-ChapGPT-20230409-中医基础-四诊之望诊

文章目录每日一问-ChapGPT系列起因每日一问-ChapGPT-20230409-中医基础-四诊之望诊中医中的望闻问切介绍&#xff0c;以及对应的名家望诊的具体细节望诊拓展当日总结每日一问-ChapGPT系列起因 近来看了新闻&#xff0c;看了各种媒体&#xff0c;抖音&#xff0c;官媒&#xff…

【数据库原理 • 四】数据库设计和规范化理论

前言 数据库技术是计算机科学技术中发展最快&#xff0c;应用最广的技术之一&#xff0c;它是专门研究如何科学的组织和存储数据&#xff0c;如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进&#xff0c;最常用的技术。 当前…

jvm调优一:从源码级别了解jvm类加载机制

目录 一、类加载运行全过程 类加载器加载类的过程 二、类加载器和双亲委派机制 类加载器类型 类加载器初始化过程 双亲委派机制 为什么要设计双亲委派机制&#xff1f; 全盘负责委托机制 一、类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c…

Kube-proxy 使用 iptables 模式时,通过 Service 服务发布入口如何到达 Pod ?

写在前面 被问到这个问题&#xff0c;整理相关的笔记当 kube-proxy 模式设置为 iptables 的时候&#xff0c;通过 SVC 服务发布入口如何到达 Pod&#xff1f;博文内容涉及&#xff1a; 问题简单介绍三种常用的服务发布方式到Pod报文路径解析 当前集群为版本为v1.25.1Demo 演示使…

linux内核结构以及内核模块编程

1、linux内核结构 1.1、单内核与微内核结构 1.1.1、什么是单内核结构和微内核结构 linux操作系统是一个单内核的结构&#xff0c;它的各个子系统之间可以直接调用 比如说文件系统、内存管理、进程管理以及网络系统和进程间通信它们互相之间可以直接调用只有一些核心的代码它…