0


【linux】信号(下)

8. 阻塞信号

**(一)信号其他相关常见概念 **

  1. 实际执行信号的处理动作称为信号递达(Delivery)
  2. 信号从产生到递达之间的状态,称为信号未决(Pending)
  3. 进程可以选择阻塞 (Block )某个信号
  4. 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作(即被阻塞的信号,进程是不会对其进行处理动作)

注意:

  1. 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作
  2. 常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里

**(二)sigset_t **

从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的

因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号 的“有效”或“无效”状态

在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有

效”和“无效”的含义是该信号是否处于未决状态

(三)信号集操作函数

#include <signal.h>

int sigemptyset(sigset_t *set);

清空set(即位图清空成0)

int sigfillset(sigset_t *set);

填满set(即位图上的32个比特位全是1)

int sigaddset (sigset_t *set, int signo);

设置set用位图表示的对应信号(即变成1)

int sigdelset(sigset_t *set, int signo);

取消set用位图表示的对应信号(即变成0)

int sigismember(const sigset_t *set, int signo);
判断set用位图表示的对应信号是否被设置(如果被设置,则返回1,如果没有,则返回0)

注意:

  1. 在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态
  2. 除了sigismember函数,其它四个函数都是成功返回0,出错返回-1
  3. sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1

(四)sigprocmask 函数

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)

#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

参数:

how 参数:一般有三个选项

oset 参数:保存了上一次的阻塞信号集

返回值:若成功则为0,若出错则为-1

(五)sigpending 函数

读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1

#include <signal.h>

sigpending (const sigset_t *set)

使用 信号操作集函数,sigprocmask 函数,sigpending 函数

9. 捕捉信号

(一)捕捉信号的时机

当我们的进程从内核态返回到用户态的时候,进行信号的检测和处理

内核态:

允许进程访问内核区的数据和代码

用户态:

只允许进程访问自己用户区的数据和代码

注意:

  1. 对于进程来说,调用操作系统的方法,就是在进程地址空间中内核区找到对应的方法
  2. 操作系统的本质:基于时钟中断的死循环(操作系统也是一个进程,管理着各种软硬件,但同时它其实也是需要别人推进执行)
  3. 计算机硬件中有一个时钟芯片,每隔很短的一段时间,向计算机发送时钟中断

(二)信号捕捉的过程

(三)sigaction 函数

sigaction函数可以读取和修改与指定信号相关联的处理动作

#include <signal.h>

int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

返回值:调用成功则返回0,出错则返回- 1

注意:

若act指针非空,则根据act修改该信号的处理动作;若oact指针非空,则通过oact传出该信号原来的处理动作

act和oact指向sigaction结构体: 结构体中有 sa_handle 类型的变量 和 sa_mask变量

sigaction 函数 代码

注意:

  1. pending位图,从1->0的变化,是在执行捕捉方法之前,先清0,再调用
  2. 信号被处理的时候,对应的信号也会被添加到block表中(比如2号信号正在执行对应的处理动作,当此时还收到2号信号时,2号信号此时被设置成阻塞信息,也可以通过sigaction结构体中的sa_mask变量设置对应的阻塞信号集),防止信号捕捉被嵌套调用

10. 可重入函数

如果一个函数,被重复进入的情况下,可能会出错,则它是不可重入函数,否则,是可重入函数(比如链表的插入,如果有两个流都用同一个插入函数,执行同一个链表,可能会在插入时发生进程切换,导致一个流的插入函数没有执行完,就进行下一个流的插入,导致内存发生泄漏)

如果一个函数符合以下条件之一则是不可重入的:

  1. 调用了malloc或free,因为malloc也是用全局链表来管理堆的
  2. 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构

11. volatile 关键字

volatile 作用:保持内存的可见性(即CPU 可以从内存读取数据),防止编译器过度优化

12. SIGCHLD信号

子进程在终止时会给父进程发SIGCHLD信号(17号信号),该信号的默认处理动作是忽略,父进程在信号处理函数中调用wait清理子进程即可

要想不产生僵尸进程还有另外一种办法:父进程调用sigaction将SIGCHLD的处理动作设置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不 会产生僵尸进程,也不会通知父进程

注意:

  1. 系统默认的忽略动作和用户用sigaction函数自定义的忽略 通常是没有区别的,但这是一个特例
  2. 后一种方法对于Linux可用,但不保证在其它UNIX系统上都可用
标签: linux

本文转载自: https://blog.csdn.net/2301_79789645/article/details/142904027
版权归原作者 小小小汐- 所有, 如有侵权,请联系我们删除。

“【linux】信号(下)”的评论:

还没有评论