Linux signal、sigaction的使用总结

article/2025/10/16 6:34:43

目录

一、什么是信号

二、信号的种类

三、信号的处理 —— signal()函数

四、信号处理 —— sigaction()函数(扑获信号)

五、扑获信号示例


一、什么是信号

        信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。比如kill、程序异常crash、段错误等

        但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程。一个信号的产生叫生成,接收到一个信号叫捕获。

二、信号的种类

        信号的名称是在头文件signal.h中定义的,信号都以SIG开头,常用的信号并不多,常用的信号如下:

更多的信号类型可在kernel目录下搜signal.h

三、信号的处理 —— signal()函数

        程序可用使用signal()函数来处理指定的信号,主要通过忽略和恢复其默认行为来工作。signal()函数的原型如下:

#include <signal.h>void (*signal(int sig, void (*func)(int)))(int);

        这是一个相当复杂的声明,耐心点看可以知道signal是一个带有sig和func两个参数的函数,func是一个类型为void (*)(int)的函数指针。

该函数返回一个与func相同类型的指针,指向先前指定信号处理函数的函数指针。准备捕获的信号的参数由sig给出,接收到的指定

信号后要调用的函数由参数func给出。其实这个函数的使用是相当简单的,通过下面的例子就可以知道。注意信号处理函数的原型

必须为void func(int),或者是下面的特殊值:

SIG_IGN : 忽略信号

SIG_DFL : 恢复信号的默认行为

说了这么多,还是给出一个例子来说明一下吧,源文件名signal.c,代码如下:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>void ouch(int sig)
{printf("\nOUCH! - I got signal %d\n", sig);// 恢复终端中断信号SIGINT的默认行为(void) signal(SIGINT, SIG_DFL);
}int main()
{// 改变终端中断信号SIGINT的默认行为,使之执行ouch函数// 而不是终止程序的执行(void) signal(SIGINT, ouch);while(1){printf("Hello World!\n");sleep(1);}return 0;
}

运行结果如下:

可以看到,第一次按下终止命令(ctrl+c)时,进程并没有被终止,面是输出OUCH! - I got signal 2,因为SIGINT的默认行为被signal()函数改变了,

当进程接受到信号SIGINT时,它就去调用函数ouch去处理,注意ouch函数把信号SIGINT的处理方式改变成默认的方式,所以当你再按一次ctrl+c时,

进程就像之前那样被终止了。

四、信号处理 —— sigaction()函数(扑获信号)

        如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:

1. 用户程序注册了SIGQUIT信号的处理函数sighandler。

2. 当前正在执行main函数,这时发生中断或异常切换到内核态。

3. 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。

4. 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,

它们之间不存在调用和被调用的关系,是两个独立的控制流程。

前面我们看到了signal()函数对信号的处理,但是一般情况下我们可以使用一个更加健壮的信号接口 —— sigaction()函数。它的原型为:

#include <signal.h>int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);

sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回-1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oldact指针非空,则通过oldact传出该信号原来的处理动作。act和oldact指向sigaction结构体: 

struct sigaction {
void     (*sa_handler)(int);
void     (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t   sa_mask;
int        sa_flags;
void     (*sa_restorer)(void);
};
  • sa_handler:信号处理器函数的地址,亦或是常量SIG_IGN、SIG_DFL之一。仅当sa_handler是信号处理程序的地址时,亦即sa_handler的
  • 取值在SIG_IGN和SIG_DFL之外,才会对sa_mask和sa_flags字段加以处理。
  • sa_sigaction:如果设置了SA_SIGINFO标志位,则会使用sa_sigaction处理函数,否则使用sa_handler处理函数。
  • sa_mask:定义一组信号,在调用由sa_handler所定义的处理器程序时将阻塞该组信号,不允许它们中断此处理器程序的执行。
  • sa_flags:位掩码,指定用于控制信号处理过程的各种选项。
    • SA_NODEFER:捕获该信号时,不会在执行处理器程序时将该信号自动添加到进程掩码中。
    • SA_ONSTACK:针对此信号调用处理器函数时,使用了由sigaltstack()安装的备选栈。
    • SA_RESETHAND:当捕获该信号时,会在调用处理器函数之前将信号处置重置为默认值(即SIG_IGN)。
    • SA_SIGINFO:调用信号处理器程序时携带了额外参数,其中提供了关于信号的深入信息

将sa_handler/sa_sigaction赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号

的编号,这样就可以用同一个函数处理多种信号。这是个回调函数,不是被main函数调用,而是被系统所调用

注意 :  sa_restorer 参数已废弃不用。

        sa_handler主要用于不可靠信号(实时信号当然也可以,只是不能带信息),sa_sigaction用于实时信号,可以带信息(siginfo_t),两者不能同时出现。sa_flags有几个选项,比较重要的有两个:SA_NODEFER 和 SA_SIGINFO,当SA_NODEFER设置时在信号处理函数执行期间不会屏蔽当前信号;当SA_SIGINFO设置时与sa_sigaction 搭配出现,sa_sigaction函数的第一个参数与sa_handler一样表示当前信号的编号,

第二个参数是一个siginfo_t 结构体,第三个参数一般不用。当使用sa_handler时sa_flags设置为0即可。

五、扑获信号示例

第一种:当信号来时,catch 信号,调用自己的处理函数后信号结束。

实例1

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \}  while( 0)void handler( int sig);
int main( int argc,  char *argv[])
{struct sigaction act;act.sa_handler = handler;sigemptyset(&act.sa_mask);act.sa_flags =  0;if (sigaction(SIGINT, &act,  NULL) <  0)ERR_EXIT( "sigaction error");for (; ;)pause();return  0;
}void handler( int sig)
{
printf( "rev sig=%d\n", sig);
}

运行结果:

XXXXXX$ ./main  (执行./main后,ctrl+c产生信号)
^Crev sig=2
^Crev sig=2
^Crev sig=2

按下ctrl+c 会一直产生信号而被处理打印recv语句。

示例2:

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \}  while( 0)void handler( int sig);int main( int argc,  char *argv[])
{struct sigaction act;act.sa_handler = handler;sigemptyset(&act.sa_mask);// 在信号处理函数执行期间屏蔽SIGQUIT信号,完毕后会抵达sigaddset(&act.sa_mask, SIGQUIT); /* 注意sigprocmask中屏蔽的信号是一直不能抵达的,除非解除了阻塞*/act.sa_flags =  0;if (sigaction(SIGINT, &act,  NULL) <  0)ERR_EXIT( "sigaction error");for (; ;)pause();return  0;
}void handler( int sig)
{printf( "rev sig=%d\n", sig);sleep( 5);
}

先按下ctrl+c ,然后马上ctrl+\,程序是不会马上终止的,即等到handler处理完毕SIGQUIT信号才会抵达。

xxx$ ./sa_mask
^Crev sig=2
^\

5s过后接着才输出Quit (core dumped),即在信号处理函数执行期间sa_mask集合中的信号被阻塞直到运行完毕。

示例3:

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \}  while( 0)void handler( int, siginfo_t *,  void *);int main( int argc,  char *argv[])
{struct sigaction act;//sa_sigaction与sa_handler只能取其一 ,sa_sigaction多用于实时信号,可以保存信息act.sa_sigaction = handler;     sigemptyset(&act.sa_mask);// 设置标志位后可以接收其他进程// 发送的数据,保存在siginfo_t结构体中act.sa_flags = SA_SIGINFO;    if (sigaction(SIGINT, &act,  NULL) <  0)ERR_EXIT( "sigaction error");for (; ;)pause();return  0;
}void handler( int sig, siginfo_t *info,  void *ctx)
{printf( "recv a sig=%d data=%d data=%d\n",sig, info->si_value.sival_int, info->si_int);
}

 第二种:当信号来时,catch 信号,调用自己的处理函数,执行完之后,继续调用信号默认的处理。

比如:当程序crash时,catch到abort信号,释放资源,然后继续abort的操作。这种方式很有用,如下图

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<signal.h>#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \}  while( 0)void signalHandler(int sig, siginfo_t* info, void* func);
static struct sigaction gOrigSigactionInt; // old的处理。
void signalHandler(int sig, siginfo_t* info, void* func)
{printf("signal num : %d\n", sig);if(sig==SIGINT)gOrigSigactionInt.sa_sigaction(sig, info, func); //继续信号的默认处理printf("end defalut...\n"); // 程序已经退出,不会打印
}int main( int argc,  char *argv[])
{printf("start main..\n");struct sigaction act;memset(&act,0,sizeof(sigaction));act.sa_flags = SA_SIGINFO|SA_ONSTACK;sigemptyset(&act.sa_mask);act.sa_sigaction = signalHandler;if (sigaction(SIGINT, &act, &gOrigSigactionInt) <  0)printf( "sigaction error");for (; ;)pause();return  0;
}

输出结果:

运行xx$./main 。后执行ctrl+c 程序退出,输出如上。

参考:

linux系统编程之信号(四):信号的捕捉与sigaction函数_Meditation-CSDN博客

Linux进程间通信(一): 信号 signal()、sigaction() - 52php - 博客园

 

------------------------------------------华丽的分界线---------------------------------------------

如果哪位兄弟准备找工作的,可以找我,有很多大厂岗位机会。待遇不错喔。+_+

 研发岗位需求:base 杭州、武汉、上海。双击查看

 

 

 


http://chatgpt.dhexx.cn/article/wgpSnWC5.shtml

相关文章

sigaction 函数

今天查一个问题&#xff0c;SIGWINCH的处理函数一直不执行&#xff0c;耽搁了不少时间&#xff0c;最后发现是另外一个地方也设置了&#xff0c;处理函数是另外的。。。。 顺便记录下使用例子&#xff1a; 使用 sigaction 函数&#xff1a; signal 函数的使用方法简单&#xf…

信号、signal 函数、sigaction 函数

文章目录 1.信号的基本概念2.利用 kill 命令发送信号3.信号处理的相关动作4.信号与 signal 函数4.1 signal 函数示例一4.2 signal 函数示例二 5.利用 sigaction 函数进行信号处理6.利用信号处理技术消灭僵尸进程 1.信号的基本概念 发送信号是进程之间常用的通信手段。信号用来…

linux sigaction详解

参看文档&#xff1a; https://blog.csdn.net/weixin_43743847/article/details/90299204https://blog.csdn.net/u010150046/article/details/77344438https://bbs.csdn.net/topics/370255407 一&#xff1a;函数原型介绍 int sigaction(int signum, const struct sigaction …

sigaction函数

#include <signal.h> int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact); //The sigaction() system call is used to change the action taken by a process on receipt of a specific signal. sigaction()系统调用用于改变进程在接收…

c语言sigaction,c语言信号处理sigaction

c语言信号处理sigaction (2011-04-18 23:45:19) 标签: c语言 信号处理 sigaction sighup it 分类: c 信号安装函数sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)的第二个参数是一个指向sigaction结构的指针(结构体名称与函数名一样,千万别弄…

linux中sigaction函数详解

一、函数原型&#xff1a;sigaction函数的功能是检查或修改与指定信号相关联的处理动作&#xff08;可同时两种操作&#xff09; int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);signum参数指出要捕获的信号类型&#xff0c;act参数指定新…

Qt线程QThread详解

目录 前言1.QThread介绍2.QThread示例一3.QThread示例二4.线程同步 前言 在程序中使用线程可以提高程序的性能、并发性、响应性和稳定性&#xff0c;使得程序设计更加灵活和简单。但是&#xff0c;线程编程也有一些挑战&#xff0c;如线程安全性和死锁等问题需要格外注意。我们…

PyQT5 多线程 QThread

PyQT5 多线程 在常规的界面软件中&#xff0c;需要将UI线程和工作线程加以区分&#xff0c;主要原因是某些工作线程很复杂且耗时&#xff0c;比如下载某个文件或者长时间的计算&#xff0c;当执行这些进程时&#xff0c;UI主进程会被阻塞&#xff0c;界面会出现未响应的状态&a…

QThread之moveToThread用法

一、怎么用 使用一个QObject作为Worker&#xff0c;并moveToThread到线程上&#xff0c;那么这个QObject生存在此线程上&#xff0c;其信号会在此线程上发射&#xff0c;其槽函数在此线程上执行。 意味着什么&#xff0c;意味着多线程操作时&#xff0c;若通过信号槽方式&…

Qt 多线程编程的 QThread 类 (详细)

本文结构如下&#xff1a; 概述优雅的开始我们的多线程编程之旅 我们该把耗时代码放在哪里&#xff1f;再谈 moveToThread()启动线程前的准备工作 开多少个线程比较合适&#xff1f;设置栈大小启动线程/退出线程 启动线程优雅的退出线程操作运行中的线程 获取状态 运行状态线程…

QT之多线程(QThread)的简单使用

一、线程简述 线程&#xff08;thread&#xff09;是操作系统能够进行运算调度的最小单位。一条线程指的是进程中一个单一顺序的控制流&#xff0c;它被包含在进程之中&#xff0c;是进程中的实际运作单位。一个进程中可以并发多个线程&#xff0c;每条线程并行执行不同的任务…

Qt 之 QThread(深入理解)

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 为了让程序尽快响应用户操作,在开发应用程序时经常会使用到线程。对于耗时操作如果不使用线程,UI界面将会长时间处于停滞状态,这种情况是用户非常不愿意看到的,我们可以用线程来解决这个问题。 前面,已…

Qt线程:QThread

一、描述 一个QThread对象管理程序内的一个线程&#xff0c;QThreads在run()中开始执行。默认情况下&#xff0c;run()通过调用exec()启动事件循环&#xff0c;并在线程内部运行一个Qt事件循环。 可以通过使用 QObject::moveToThread() 将对象移动到线程来使用它们。 class W…

PyQt中的多线程QThread示例

PyQt中的多线程 一、PyQt中的多线程二、创建线程2.1 设计ui界面2.2 设计工作线程2.3 主程序设计 三、运行结果示例 一、PyQt中的多线程 传统的图形用户界面应用程序都只有一个执行线程&#xff0c;并且一次只执行一个操作。如果用户从用户界面中调用一个比较耗时的操作&#x…

【Qt】Qt的线程(两种QThread类的详细使用方式)

Qt提供QThread类以进行多任务处理。与多任务处理一样&#xff0c;Qt提供的线程可以做到单个线程做不到的事情。例如&#xff0c;网络应用程序中&#xff0c;可以使用线程处理多种连接器。 QThread继承自QObject类&#xff0c;且提供QMutex类以实现同步。线程和进程共享全局变量…

Qt - 一文理解QThread多线程(万字剖析整理)

目录 为什么需要多线程QThread使用方法new QThread Class & Override run()new Object Class & moveToThread(new QThread) connect事件循环源码分析如何正确退出线程堆栈大小优先级线程间通讯线程同步互斥锁读写锁信号量条件变量 可重入与线程安全QObject的可重入性开…

Qt 线程中QThread的使用

文章目录 Qt 线程中QThread的使用1. 线程类 QThread1.1 常用共用成员函数1.2 信号槽1.3 静态函数1.4 任务处理函数 2. 使用方式 12.2 示例代码3. 使用方式 23.1 操作步骤3.2 示例代码 Qt 线程中QThread的使用 在进行桌面应用程序开发的时候&#xff0c; 假设应用程序在某些情况…

Qt之QThread(深入理解)

简述 为了让程序尽快响应用户操作&#xff0c;在开发应用程序时经常会使用到线程。对于耗时操作如果不使用线程&#xff0c;UI界面将会长时间处于停滞状态&#xff0c;这种情况是用户非常不愿意看到的&#xff0c;我们可以用线程来解决这个问题。 前面&#xff0c;已经介绍了…

Qt之QThread介绍(常用接口及实现、自动释放内存、关闭窗口时停止线程运行、同步互斥)

在程序设计中&#xff0c;为了不影响主程序的执行&#xff0c;常常把耗时操作放到一个单独的线程中执行。Qt对多线程操作有着完整的支持&#xff0c;Qt中通过继承QThread并重写run()方法的方式实现多线程代码的编写。针对线程之间的同步与互斥问题&#xff0c;Qt还提供了QMutex…

Qt线程QThread开启和安全退出

1、线程开启 Qt中&#xff0c;开启子线程&#xff0c;一般有两种方法&#xff1a; a, 定义工作类worker: worker继承 QThread, 重写run函数&#xff0c;在主线程中实例化worker&#xff0c;把耗时工作放进worker的run函数中完成&#xff0c;结束后&#xff0c;往主线程中发信…