Linux信号透彻分析理解与各种实例讲解( 二 )


(4)信号处理函数执行完毕
执行处理函数 , 本次信号在进程中响应完毕 。
在第4步 , 只简单的描述了信号处理函数执行完毕 , 就完成了本次信号的响应 , 但这个信号处理函数空间是怎么处理的呢? 内核栈与用户栈是怎么工作的呢? 这就涉及到了信号处理函数的过程 。
信号处理函数的过程:
(1)注册信号处理函数
信号的处理是由内核来代理的 , 首先程序通过sigal或sigaction函数为每个信号注册处理函数 , 而内核中维护一张信号向量表 , 对应信号处理机制 。 这样 , 在信号在进程中注销完毕之后 , 会调用相应的处理函数进行处理 。
(2)信号的检测与响应时机
在系统调用或中断返回用户态的前夕 , 内核会检查未决信号集 , 进行相应的信号处理 。
(3)处理过程:
程序运行在用户态时->进程由于系统调用或中断进入内核->转向用户态执行信号处理函数->信号处理函数完毕后进入内核->返回用户态继续执行程序
首先程序执行在用户态 , 在进程陷入内核并从内核返回的前夕 , 会去检查有没有信号没有被处理 , 如果有且没有被阻塞就会调用相应的信号处理程序去处理 。 首先 , 内核在用户栈上创建一个层 , 该层中将返回地址设置成信号处理函数的地址 , 这样 , 从内核返回用户态时 , 就会执行这个信号处理函数 。 当信号处理函数执行完 , 会再次进入内核 , 主要是检测有没有信号没有处理 , 以及恢复原先程序中断执行点 , 恢复内核栈等工作 , 这样 , 当从内核返回后便返回到原先程序执行的地方了 。
信号处理函数的过程大概是这样了 。
需要C/C++ Linux高级服务器架构师学习资料后台私信“资料”(包括C/C++ , Linux , golang技术 , Nginx , ZeroMQ , MySQL , Redis , fastdfs , MongoDB , ZK , 流媒体 , CDN , P2P , K8S , Docker , TCP/IP , 协程 , DPDK , ffmpeg等)
Linux信号透彻分析理解与各种实例讲解文章插图
第三部分: 基本的信号处理函数
首先看一个两个概念: 信号未决与信号阻塞
信号未决: 指的是信号的产生到信号处理之前所处的一种状态 。 确切的说 , 是信号的产生到信号注销之间的状态 。
信号阻塞: 指的是阻塞信号被处理 , 是一种信号处理方式 。
1. 信号操作
信号操作最常用的方法是信号的屏蔽 , 信号屏蔽主要用到以下几个函数:
int sigemptyset(sigset_t *set);int sigfillset(sigset_t *set);int sigaddset(sigset_t *set,int signo);int sigdelset(sigset_t *set,int signo);int sigismemeber(sigset_t* set,int signo);int sigprocmask(int how,const sigset_t*set,sigset_t *oset); 信号集 , 信号掩码 , 未决集
信号集: 所有的信号阻塞函数都使用一个称之为信号集的结构表明其所受到的影响 。
信号掩码:当前正在被阻塞的信号集 。
未决集: 进程在收到信号时到信号在未被处理之前信号所处的集合称为未决集 。
可以看出 , 这三个概念没有必然的联系 , 信号集指的是一个泛泛的概念 , 而未决集与信号掩码指的是具体的信号状态 。
对于信号集的初始化有两种方法: 一种是用sigemptyset使信号集中不包含任何信号 , 然后用sigaddset把信号加入到信号集中去 。
另一种是用sigfillset让信号集中包含所有信号 , 然后用sigdelset删除信号来初始化 。
sigemptyset()函数初始化信号集set并将set设置为空 。
sigfillset()函数初始化信号集 , 但将信号集set设置为所有信号的集合 。
sigaddset()将信号signo加入到信号集中去 。
sigdelset()从信号集中删除signo信号 。
sigprocmask()将指定的信号集合加入到进程的信号阻塞集合中去 。 如果提供了oset,那么当前的信号阻塞集合将会保存到oset集全中去 。