【linux】信号的保存和递达处理(二)

简介: 【linux】信号的保存和递达处理

 四个交点(四次身份切换)


       在用户态中因为一些原因陷入内核,执行系统调用后,在内核态中再进行信号的检测过程,再由内核态切换到用户态执行方法,完毕后再切换身份回到内核态,通过信号检测结束后,再身份切换,回到进程执行流中上次中断的地方。


三、sigset_t 信号集        

       我们知道信号是在进程的pcb中,即内核中。所以用户级操作难免会困难一些。所以sigset_t 信号集就是为了更好的在用户级操作信号所产生的类型。sigset_t 信号集包括 pending信号集、信号屏蔽字(block信号集)。sigset_t 底层就是一个大数组实现的位图结构。


信号集操作函数:


#include <signal.h>

int sigemptyset(sigset_t *set);                  //初始化set信号集

int sigfillset(sigset_t *set);                        //将信号集set全部设置为1

int sigaddset (sigset_t *set, int signo);     //往set信号集添加信号

int sigdelset(sigset_t *set, int signo);       //删除set信号集中的信号

这四个函数都是成功返回0,出错返回-1 。

int sigismember(const sigset_t *set, int signo);        //判断信号是否在set中

sigismember 是一个布尔函数 , 用于判断一个信号集的有效信号中是否包含某种信号, 若包含则返回 1, 不包含则返回 0, 出错返回 -1 。

sigprocmask

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

#include <signal.h>

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

how就是下面的几种方式:

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

sigpending

#include <signal.h>

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

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

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

下面我们利用上面所学,来实现一个观察pending信号集,通过信号屏蔽子来观察pending信号集的变化:

#include <iostream>
#include <vector>
#include <signal.h>
#include <unistd.h>
// #define BLOCK_SIGNAL 2
#define MAX_SIGNUM 31
using namespace std;
static vector<int> sigarr = {2}; 
//输出pending信号集
static void show_pending(const sigset_t &pending)
{
    for(int signo = MAX_SIGNUM; signo >= 1; signo--)
    {
        if(sigismember(&pending, signo))
        {
            cout << "1";
        }
        else cout << "0";
    }
    cout << "\n";
}
//递达自定义动作
static void myhandler(int signo)
{
    cout << signo << " 号信号已经被递达!!" << endl;
}
int main()
{
    for(const auto &sig : sigarr) signal(sig, myhandler);
    // 1. 先尝试屏蔽指定的信号
    sigset_t block, oblock, pending;
    // 1.1 初始化
    sigemptyset(&block);
    sigemptyset(&oblock);
    sigemptyset(&pending);
    // 1.2 添加要屏蔽的信号
    for(const auto &sig : sigarr) sigaddset(&block, sig);  //批量化屏蔽
    // 1.3 开始屏蔽,设置进内核(进程)
    sigprocmask(SIG_SETMASK, &block, &oblock);
    // 2. 遍历打印pengding信号集
    int cnt = 10;
    while(true)
    {
        // 2.1 初始化
        sigemptyset(&pending);
        // 2.2 获取它
        sigpending(&pending);
        // 2.3 打印它
        show_pending(pending);
        // 3. 慢一点
        sleep(1);
        if(cnt-- == 0)
        {
            sigprocmask(SIG_SETMASK, &oblock, &block); // 一旦对特定信号进行解除屏蔽,一般OS要至少立马递达一个信号!
            cout << "恢复对信号的屏蔽,不屏蔽任何信号\n";
        }
    }
}

四、信号的处理细节

4.1 对于同类型信号的处理

       当我们正在递达一个信号期间,同类型的信号无法被递达!(信号的处理细节)


       当信号正在被递达中,又来了同类型的信号,此时当前信号会被加入到进程的信号屏蔽字,且会将pending中该信号对应的那一位由0变为1。(因为该信号被递达前,会将pending中对应的那一位由1改为0),若结束递达后,同类型仍发送,则会继续重复上面的动作。但若结束递达后,同类型的信号没有发送了,进程就只会再捕捉一次,将pending中的1改为0。递达后则继续检其他信号进行递达。


       进程处理信号的原则是穿行的处理同类型的信号,不允许递归处理!


4.2 可重入函数和不可重入函数

       举例说明:


     在main执行流中,没有头结点的单链表进行头插,如上图所示:在执行到第一步时,此时被信号中断,结果导致main中还没有执行完又进入insert()中,最后回到main执行流中,再执行完剩下的代码结果导致内存泄漏等问题。


       1.一般而言,main执行流和信号捕捉执行流是两个执行流!


       2.如果在main中,和在handler中,该函数被反复进入:1出现问题的就是不可重入函数;2.没有出现问题的就是可重入函数。当然可重入和不可重入只是他们的特性,没有好坏之分。


4.3 volatile关键字

       我们在读取变量的值时,一般会从内存中读取,但是由于编译器的优化,就会将内存中的值加载到cpu的寄存器中,从而之后访问该变量的值只会从寄存器中读取,如果这个变量的值被修改了,自然而然内存上的值也被修改了,但是寄存器中的值仍然没有变化,还是修改之前的值,所以为了避免这种优化产生的后果,我们就会在变量前加上volatile,意为一直从内存中读取值!


总结:


       我们了解了信号的保存原来是通过进程pcb中的pending、block位图,handler函数指针数组来进行保存,从而信号递达。  


目录
相关文章
|
3月前
|
Linux 调度
Linux0.11 信号(十二)(下)
Linux0.11 信号(十二)
25 1
|
3月前
|
存储 Linux 调度
|
3月前
|
存储 Unix Linux
Linux0.11 信号(十二)(上)
Linux0.11 信号(十二)
32 0
|
3月前
|
Linux
|
4月前
|
安全 小程序 Linux
Linux中信号是什么?Ctrl + c后到底为什么会中断程序?
信号在进程的学习中是一个非常好用的存在,它是软件层次上对中断机制的一种模拟,是异步通信方式,同时也可以用来检测用户空间到底发生了什么情况,然后系统知道后就可以做出相应的对策。
123 6
|
4月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
214 2
|
4月前
|
存储 NoSQL Unix
【Linux】进程信号(下)
【Linux】进程信号(下)
41 0
|
4月前
|
安全 Linux Shell
【Linux】进程信号(上)
【Linux】进程信号(上)
47 0
|
5月前
|
Linux Shell
蓝易云 - 【Linux-Day8- 进程替换和信号】
这两个概念在Linux系统编程和shell脚本编写中都非常重要,理解它们可以帮助你更好地理解和控制Linux系统的行为。
41 9
|
5月前
|
Linux
【Linux】进程信号_1
【Linux】进程信号_1
27 0