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

Linux信号

信号

    • 信号的基本概念
    • 信号的分类、Linux 提供的各种不同的信号及其作用
    • 进程对信号的处理
    • 向进程发送信号
    • 信号集
    • 获取信号的描述信息
    • 信号掩码(阻塞信号传递)
    • 阻塞等待信号 sigsuspend()
    • 确定进程中处于等待状态的是哪些信号
    • 实时信号

信号的基本概念

信号可以由“谁”发出

  1. 硬件发生异常,即硬件检测到错误条件并通知内核,随即再由内核发送相应的信号给相关进程。硬件检测到异常的例子包括执行一条异常的机器语言指令,诸如,除数为 0、数组访问越界导致引用了无法访问的内存区域等,这些异常情况都会被硬件检测到,并通知内核、然后内核为该异常情况
    发生时正在运行的进程发送适当的信号以通知进程。
  2. 用于在终端下输入了能够产生信号的特殊字符。譬如在终端上按下 CTRL + C 组合按键可以产生中断信号(SIGINT),通过这个方法可以终止在前台运行的进程;按下 CTRL + Z 组合按键可以产生暂停信号(SIGCONT),通过这个方法可以暂停当前前台运行的进程。
  3. 。。。

信号由谁处理、怎么处理

  1. 忽略信号。也就是说,当信号到达进程后,该进程并不会去理会它、直接忽略,就好像是没有出该信号,信号对该进程不会产生任何影响。
  2. 捕获信号。当信号到达进程后,执行预先绑定好的信号处理函数。为了做到这一点,要通知内核在某种信号发生时,执行用户自定义的处理函数,该处理函数中将会对该信号事件作出相应的处理,Linux 系统提供了 signal()系统调用可用于注册信号的处理函数,将会在后面向大家介绍。
  3. 执行系统默认操作。进程不对该信号事件作出处理,而是交由系统进行处理,每一种信号都会有其对应的系统默认的处理方式。

信号本质上是 int 类型数字编号

/* Signals. */
#define SIGHUP 1 /* Hangup (POSIX). */
#define SIGINT 2 /* Interrupt (ANSI). */
#define SIGQUIT 3 /* Quit (POSIX). */
#define SIGILL 4 /* Illegal instruction (ANSI). */
#define SIGTRAP 5 /* Trace trap (POSIX). */
#define SIGABRT 6 /* Abort (ANSI). */

信号的分类、Linux 提供的各种不同的信号及其作用

可靠信号与不可靠信号
不可靠信号
Linux 下的不可靠信号问题主要指的是信号可能丢失。在 Linux 系统下,信号值小于 SIGRTMIN(34)的信号都是不可靠信号.如果在短时间内多次发送同一信号,某些实现可能会丢失部分信号,尤其是对 SIGKILL 和 SIGSTOP 等特殊信号,这些信号无法被阻塞、捕捉或忽略。

可靠信号
支持排队,不会丢失。

实时信号与非实时信号
实时信号与非实时信号其实是从时间关系上进行的分类,与可靠信号与不可靠信号是相互对应的,非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。实时信号保证了发送的多个信号都能被接收,实时信号是 POSIX 标准的一部分,可用于应用进程。

进程对信号的处理

signal()和 sigaction()两个函数

向进程发送信号

kill()系统调用之外,Linux 系统还提供了系统调用 killpg()以及库函数 raise(),也可用于实现发送信号的功能

alarm()和 pause()

信号集

# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{unsigned long int __val[_SIGSET_NWORDS];
} sigset_t;
  1. 初始化信号集
    sigemptyset()和 sigfillset()用于初始化信号集。sigemptyset()初始化信号集,使其不包含任何信号;而sigfillset()函数初始化信号集,使其包含所有信号(包括所有实时信号)
  2. 向信号集中添加/删除信号
    分别使用 sigaddset()和 sigdelset()函数向信号集中添加或移除一个信号
  3. 测试信号是否在信号集中

获取信号的描述信息

可以使用 sys_siglist[SIGINT]来获取对 SIGINT 信号的描述。

strsignal()函数 psignal()函数

信号掩码(阻塞信号传递)

内核为每一个进程维护了一个信号掩码(其实就是一个信号集),即一组信号。当进程接收到一个信号掩码中定义的信号时,该信号将会被阻塞、无法传递给进程进行处理,那么内核会将其阻塞,直到该信号从信号掩码中移除,内核才会把该信号传递给进程从而得到处理。

向信号掩码中添加一个信号,通常有如下几种方式:
⚫ 当应用程序调用 signal()或 sigaction()函数为某一个信号设置处理方式时,进程会自动将该信号添加到信号掩码中,这样保证了在处理一个给定的信号时,如果此信号再次发生,那么它将会被阻塞;当然对于 sigaction()而言,是否会如此,需要根据 sigaction()函数是否设置了 SA_NODEFER 标志而定;当信号处理函数结束返回后,会自动将该信号从信号掩码中移除。
⚫ 使用 sigaction()函数为信号设置处理方式时,可以额外指定一组信号,当调用信号处理函数时将该组信号自动添加到信号掩码中,当信号处理函数结束返回后,再将这组信号从信号掩码中移除;通过 sa_mask 参数进行设置,参考 8.4.2 小节内容。
⚫ 除了以上两种方式之外,还可以使用 sigprocmask()系统调用,随时可以显式地向信号掩码中添加/移除信号。

阻塞等待信号 sigsuspend()

上一小节已经说明,更改进程的信号掩码可以阻塞所选择的信号,或解除对它们的阻塞。使用这种技术可以保护不希望由信号中断的关键代码段。如果希望对一个信号解除阻塞后,然后调用 pause()以等待之前被阻塞的信号的传递。

 sigset_t new_set, old_set;/* 信号集初始化 */sigemptyset(&new_set);sigaddset(&new_set, SIGINT);/* 向信号掩码中添加信号 */if (-1 == sigprocmask(SIG_BLOCK, &new_set, &old_set))exit(-1);/* 受保护的关键代码段 */....../**********************//* 恢复信号掩码 */if (-1 == sigprocmask(SIG_SETMASK, &old_set, NULL))exit(-1);pause();/* 等待信号唤醒 */

执行受保护的关键代码时不希望被 SIGINT 信号打断,所以在执行关键代码之前将 SIGINT 信号添加到进程的信号掩码中,执行完毕之后再恢复之前的信号掩码。最后调用了 pause()阻塞等待被信号唤醒,如果此时发生了信号则会被唤醒、从 pause 返回继续执行;考虑到这样一种情况,如果信号的传递恰好发生在第二次调用 sigprocmask()之后、pause()之前,如果确实发生了这种情况,就会产生一个问题,信号传递过来就会导致执行信号的处理函数,而从处理函数返回后又回到主程序继续执行,从而进入到 pause()被阻塞,知道下一次信号发生时才会被唤醒,这有违代码的本意。
虽然信号传递发生在这个时间段的可能性并不大,但并不是完全没有可能,这必然是一个缺陷,要避免这个问题,需要将恢复信号掩码和 pause()挂起进程这两个动作封装成一个原子操作,这正是 sigsuspend()系统调用的目的所在

确定进程中处于等待状态的是哪些信号

确定进程中处于等待状态的是哪些信号,可以使用 sigpending()函数获取

实时信号

等待信号集只是一个掩码,仅表明一个信号是否发生,而不能表示其发生的次数。换言之,如果一个同一个信号在阻塞状态下产生了多次,那么会将该信号记录在等待信号集中,并在之后仅传递一次(仅当做发生了一次),这是标准信号的缺点之一。
实时信号较之于标准信号,其优势如下:
⚫ 实时信号的信号范围有所扩大,可应用于应用程序自定义的目的,而标准信号仅提供了两个信号可用于应用程序自定义使用:SIGUSR1 和 SIGUSR2。
⚫ 内核对于实时信号所采取的是队列化管理。如果将某一实时信号多次发送给另一个进程,那么将会多次传递此信号。相反,对于某一标准信号正在等待某一进程,而此时即使再次向该进程发送此信号,信号也只会传递一次。
⚫ 当发送一个实时信号时,可为信号指定伴随数据(一整形数据或者指针值),供接收信号的进程在它的信号处理函数中获取。
⚫ 不同实时信号的传递顺序得到保障。如果有多个不同的实时信号处于等待状态,那么将率先传递具有最小编号的信号。换言之,信号的编号越小,其优先级越高,如果是同一类型的多个信号在排队,那么信号(以及伴随数据)的传递顺序与信号发送来时的顺序保持一致。


http://www.mrgr.cn/news/61065.html

相关文章:

  • CSP-J2024入门级T3:小木棍
  • 大白话讲解Spring对数据源和事务管理以及多数据源配置
  • 国际数字影像产业园:智慧园区中的创新协作平台
  • java 聚合和规约的区别
  • C++学习,算法
  • 智能化超声波影像分析,优化医疗决策的开源AI解决方案
  • 基于深度学习算法的动物检测系统(含PyQt+代码+训练数据集)
  • 网管平台(进阶篇):网管软件的配置方式
  • 深入理解所有权与借用——借用与生命周期管理
  • OPA548T 数据手册OPA548 高电压、大电流运算放大器芯片
  • 拥抱中国企业数智化周期,IT产业投资切入点何在?
  • python基础(类、实例、属性、方法)
  • 音频翻译怎么操作?亲测实用的4个转换工具,推荐收藏
  • Python字典到JSON字符串的转换
  • 常用方法:枚举类型
  • golang包导入注意事项
  • 山峰为您的设备选择合适的油封
  • IDEA使用正则批量替换(理论上JetBrains全家桶都适用)
  • 基础IO -- 简单封装库(文件操作)
  • CRM客户关系管理系统:全方位提升销售效能的利器