网站开发毕业设计评审表,2018做分享网站,网站推广是什么,php网站开发最低配置这一篇的篇幅可能有点长#xff0c;如果已经了解了以下两个知识点的同学可以自行跳到第三部分——信号屏蔽的实现。
不太了解的同学希望你们能够静下心来看完#xff0c;相信一定会有不小的收获。那么话不多说#xff0c;我们这就开始啦#xff01;#xff01;#xff0…这一篇的篇幅可能有点长如果已经了解了以下两个知识点的同学可以自行跳到第三部分——信号屏蔽的实现。
不太了解的同学希望你们能够静下心来看完相信一定会有不小的收获。那么话不多说我们这就开始啦
想要明白如何实现信号屏蔽和信号捕捉我们就要先了解两个知识点 信号如何在进程中传递信号的三大行为和五种动作 而想要了解这两个知识点我们又得先来了解一些基础概念 PCB中有两个信号集分别是未决信号集和屏蔽字这两个信号集都是由若干个位构成的系统支持多少个信号信号集就有多少个信号位每个信号都走自己对应的那个位置信号位的位码分别是0或1每个信号位就好比一扇门1表示禁止通过0表示允许通过默认情况下每个信号位的位码均为0也就是开门状态未决信号集的信号位位码由系统设置屏蔽字的信号位位码可由用户自己设置以屏蔽某些特定信号 在了解了这些基础概念之后我们就可以来了解信号在进程中的传递过程了以2号信号—SIGINT举例具体情况如下图所示
信号在进程中的传递过程
从以上图中我们可以得出一些有趣的信息那就是Unix经典信号(1-31号信号)是不支持排队的同一时间最多处理两个信号其余的信号都会被直接丢弃掉
但要注意的是自定义信号(34-64号信号)是支持排队的采用自定义信号队列来帮助信号进行排队。为什么这两大类信号会有这样的差别呢这就要涉及到这两大类信号的功能了。 我们知道1-31号信号大多是用来杀死或挂起进程的而进程你杀一次或者挂起一次就行了你要是支持排队的话那就会变成来一个信号杀一次来一个信号又杀一次杀完之后继续杀咋的杀一次不过瘾还要鞭尸啊这也就是Unix经典信号不支持排队的原因。 而34-64号信号为什么排队呢举个例子相信大家都用遥控器换过台遥控器也是通过信号来完成任务这里的信号涉及到的就是自定义信号你连按十次换台键难道电视只给你换一个台吗这也就是自定义信号支持排队的原因它们的工作主要是绑定任务或事件所以需要支持排队。 信号的三大行为和五种动作
三大行为与五种动作其实就是我们讲的handler里面的详细内容具体如下图所示 所谓的三大行为就是SIG_DFL—默认行为、SIG_IGN—忽略行为、SIG_ACTION—捕捉行为 默认情况下如果我们不做特殊处理的话信号都会执行默认行为五种动作也就是默认行为中的五种默认处理动作 这些相信大家都能从上面的图中看出来这里我们讲一讲捕捉行为
其实也很简单捕捉函数是由用户实现的我们可以在这个函数中实现我们想要完成的功能。
当进程的PCB接收到对应信号时就会触发对应的捕捉函数在让信号失效的同时也会完成我们所要求的功能
由此也就引出了本篇博客的正题让信号失效的三种方式 信号忽略将信号挡在未决信号集外或让信号进入忽略行为信号屏蔽将信号延迟递达阻塞在未决信号集和屏蔽字之间信号捕捉让信号进入捕捉行为并使其执行用户自定义的捕捉函数 但要注意的是不是所有的信号都能够被失效因为一旦出现进程出现错误或者是出现病毒很可能系统发现了问题但由于信号都被屏蔽无法杀死对应进程或病毒
所以操作系统保留了两个高级操作信号他们不按照上述流程在进程中传递且无法被失效这两个信号就是9号信号—SIGKILL与19号信号—SIGSTOP一个用来杀死进程一个用来挂起进程
信号屏蔽的实现
先和大家说一句下面所需要的特殊数据类型和相关函数的头文件都是 #includesignal.h
#includesignal.h信号头文件定义信号符号常量信号结构以及信号操作函数原型。
接下来我们来了解一下实现信号屏蔽需要用到的特殊数据类型与相关函数
信号屏蔽相关的特殊数据类型与函数
实现信号屏蔽需要用到一个特殊的数据类型叫做信号集类型也就是sigset_t这里我定义一个信号集类型的变量set也就是sigset_t set;
再定义一个变量int signo; signo——信号序号
以下是实现信号屏蔽常用的相关函数
函数功能返回值int sigemptyset(set);初始化信号集将所有信号位的位码置为0也就是允许通过状态 成功返回0失败返回-1并设置errno以返回失败原因 int sigfillset(set);初始化信号集将所有信号位的位码置为1也就是禁止通过状态成功返回0失败返回-1并设置errno以返回失败原因int sigdelset(set , int signo);将信号集中序号为signo的信号位的位码置为0也就是允许通过状态(请看下方的注释①)成功返回0失败返回-1int sigaddset(set , int signo);将信号集中序号为signo的信号位的位码置为1也就是禁止通过状态成功返回0失败返回-1int sigismember(set , int signo);获取信号集中序号为signo的信号位的位码返回对应信号位的位码即0或1int sigprocmask(替换方式 , 新信号集 , 是否传出原有屏蔽字)(参数介绍请看注释②)用新的信号集替换旧的屏蔽字得到新的屏蔽字替换成功返回0失败返回-1
注释①注意这里的signo要传入的是信号名而不是信号编号就比如我们想要屏蔽SIGINT信号但是不同系统下同样信号的信号编号可能不一样可能系统1中SIGINT的信号编号是2系统2中SIGINT的信号编号是3所以我们在传入时都是直接传入信号名而不是信号编号
注释②sigprocmask函数的参数介绍
参数变量类型解释替换方式------ 替换方式有以下三种 SIG_SETMASK (把新信号集直接覆盖到原有屏蔽字上)、 SIG_BLOCK (将新信号集和原有屏蔽字的对应信号位的位码进行位或运算) SIG_UNBLOCK (将新信号集中每个信号位的位码取反后和原有屏蔽字进行位与运算)这三种替换方式中最常用的就是SIG_SETMASK 新信号集sigset_t*用户设置的新信号集用于替换旧的屏蔽字以屏蔽某些特定信号是否传出原有屏蔽字sigset_t*传入NULL表示不传出原有屏蔽字传入sigset_t* 类型的变量表示传出原有屏蔽字
了解完这些之后我们就可以来实现信号屏蔽了
实现信号屏蔽的具体步骤
我们来完成一个功能——让进程屏蔽SIGINT信号具体如下所示
代码实现
//sig_shield.c#includestdio.h
#includestdlib.h
#includeunistd.h
#includesignal.hint main(void)
{sigset_t newset;//初始化一个信号集sigemptyset(newset);//将所有信号位位码全部置为0sigaddset(newset , SIGINT);//屏蔽SIGINT信号将其对应信号位的位码置为1int bitcode sigismember(newset , SIGINT);//利用sigismember函数检验位码是否被置为1if(bitcode 1){printf(新信号集设置成功\n);}else{printf(新信号集设置失败\n);exit(1);}sigprocmask(SIG_SETMASK , newset , NULL);//用新信号集替换屏蔽字以得到新的屏蔽字,不传出旧的屏蔽字//写一个死循环让进程一次睡一秒同时测试终端界面下CTRLC能否杀死该进程int count 0;while(1){sleep(1);count;printf( count %d\n , count);}exit(0);
}
图解 我们可以很清楚地发现CTRLC所发出的SIGINT信号被屏蔽掉了CTRL\发出的SIGQUIT信号成功过杀死了进程以上简单的信号屏蔽就实现了是不是很轻松呢
接下来我们来了解一下信号捕捉的相关知识和具体实现吧
信号捕捉的实现
在之前的讲解中我们提到过信号一共有三种行为分别是SIG_DFL(默认行为)、SIG_IGN(忽略行为)、SIG_ACTION(捕捉行为)
正常情况下信号是走默认行为的想要实现信号捕捉也就意味着我们要对进行信号行为选择让其走入信号行为而要进入捕捉行为代表信号要进入handler这也就意味着信号必须要先通过未决信号集和屏蔽字变成递达态才能够进入处理流程
信号捕捉相关的特殊数据类型与函数
这里就涉及到了一个结构体叫做struct sigaction , 我们来介绍一下这个结构体里的成员
成员变量类型功能解释sa_handler 函数指针类型,参数类型为int void (*sa_handler)(int) 行为选择可传入三种参数——分别是SIG_DFL(默认行为)、SIG_IGN(忽略行为)、捕捉函数的地址。需要注意的是捕捉函数的写法必须是【void 函数名(int)】来匹配sa_handler的变量类型sa_flagsint指定对信号进行什么特殊处理一般情况下传0表示默认选项更多的参数大家可以去自己搜索一下这里就不做过多介绍了sa_masksigset_t作为临时屏蔽字阻挡多个同类信号同时进入handler。使用前需要初始化sa_mask是一个信号集当某个信号通过屏蔽字进入sa_handler函数之前该信号集里面的对应信号被加入到进程的信号掩码当中当sa_handler函数执行完之后屏蔽字复位为原先值。这样就保证了某个信号正在被处理时如果其他同类信号再次传入就会被阻塞
这里我们定义两个变量分别是struct sigaction act;和 struct sigaction old_act;
然后我们就要了解一个函数用于改变信号的行为这个函数和上面的结构体同名叫做sigaction函数
头文件#includesignal.h功能将目标信号的行为替换为用户要求的行为可传出原有行为语法int sigaction(int signo , struct sigaction *act , struct sigaction *old_act);返回值成功返回0失败返回-1
了解完这些之后我们就可以实现信号捕捉了
实现信号捕捉的具体步骤
代码实现
//Sig_Catch.c#include stdio.h
#include stdlib.h
#include unistd.h
#include string.h
#include sys/types.h
#include signal.h//这个n不是我们填入的是系统填入的信号序号
void sig_do(int n){printf(成功捕捉%d信号\n,n);
}int main()
{struct sigaction act , old_act;//创建两个sigaction类型的结构体act.sa_handler sig_do;//令信号从默认行为变成捕捉行为act.sa_flags 0;//默认选项不做特殊处理sigemptyset(act.sa_mask);//初始化结构体中的sa_masksigaction(SIGINT , act , old_act);//将信号SIGINT的行为替换为捕捉行为并传出原有行为//写一个死循环每次睡一秒看CTRLC能否杀死该进程while(1){sleep(1);}return 0;
}
图解 以上就是本篇博客的全部内容了大家有什么地方没有看懂的话可以在评论区留言给我咱要力所能及的话就帮大家解答解答
今天的学习记录到此结束啦咱们下篇文章见ByeBye