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

阜南网站建设上海共富新村网站建设

阜南网站建设,上海共富新村网站建设,宁波网站设计价格,百度一下你就知道官页信号保存与信号捕捉 一、信号保存1. 信号的发送2. 理解信号保存#xff08;1#xff09;信号保存原因#xff08;2#xff09;信号保存概念 3. 信号保存系统接口#xff08;1#xff09;sigset_t#xff08;2#xff09;sigprocmask()#xff08;3#xff09;sigpend… 信号保存与信号捕捉 一、信号保存1. 信号的发送2. 理解信号保存1信号保存原因2信号保存概念 3. 信号保存系统接口1sigset_t2sigprocmask()3sigpending()4signal()5测试系统接口 二、信号捕捉处理1. 信号的处理2. 理解用户态和内核态3. 信号的捕捉4. 系统调用1sigaction()2pending 表的置0顺序3struct sigaction 中的 sa_mask 字段 三、信号扩展1. 可重入函数2. volatile3. SIGCHLD 信号 一、信号保存 1. 信号的发送 那么在学习信号保存之前我们先了解一下信号的发送我们知道普通信号一共有31个如下 但是这个31就非常特殊对于普通信号而言对于进程而言自己有还是没有收到哪一个信号。实际上我们发送信号是给进程发具体点就是给进程的 PCB 发所以 task_struct 中必定有维护信号的字段那么在 task_struct 中其实只需要维护一个整数即可因为一个整数有 32 个比特位而我们忽略第一位从第2位开始到第32位一共31个比特位就分别表示31种信号也就是说用0、1来描述信号用位图管理普通信号 所以 比特位的内容是0还是1表明是否收到比特位的位置(第几个)表示信号的编号所谓的发送信号本质就是操作系统去修改 task_struct 的信号位图对应的比特位 那么为什么必须是操作系统向进程PCB中写入呢因为操作系统是进程的管理者只有它有资格才能修改 task_struct 内部的属性所以这就是为什么只有操作系统才能有资格给进程发信号 2. 理解信号保存 1信号保存原因 信号为什么要保存呢因为进程收到信号之后可能不会立即处理这个信号可能正在处理更重要的事情所以信号不会被处理就要有一个时间窗口所以信号就要被保存。 2信号保存概念 实际执行信号的处理动作称为信号递达(Delivery)信号从产生到递达之间的状态,称为信号未决(Pending)进程可以选择阻塞 (Block )某个信号被阻塞的信号产生时将保持在未决状态直到进程解除对此信号的阻塞才执行递达的动作注意阻塞和忽略是不同的只要信号被阻塞就不会递达而忽略是在递达之后可选的一种处理动作。 所以进程的 task_struct 中不仅要保存信号的状态还要保存信号的阻塞状态而且信号的范围是 1~31每一种信号都要有自己的一种处理方法所以在 task_struct 中还要为每一个信号维护一张 handler 表这张表是函数指针数组比如数组中的内容是 typedef void (*handler_t)(int); 类型数组名为 handler_t handler[31];所以它就是一个函数指针数组里面放的就是指向方法的地址而下标就是信号的编号那么当我们捕捉对应信号后自定义的方法就将我们的方法的地址填入对应的位置即可如下图 而上面的 pending 表就是一个位图表示信号未决的状态 那么 block 表也是一个位图1表示被阻塞0表示未阻塞。一旦阻塞了某个信号在该信号没有被解除阻塞之前即便收到了该信号对应的信号也不会被操作系统进行递达。 所以 pending 表记录当前进程是否收到了信号以及收到了哪些信号block 表记录特定信号是否被屏蔽handler 表记录每种信号的处理方法。 所以对于某个信号对应上面的三张表中我们应该横向看是否被屏蔽、是否收到、对应方法。所以我们对于信号的学习无论给我们提供多少系统接口都是围绕这三张表获取或者修改。 3. 信号保存系统接口 上面的两张表中block 和 pending 是两张位图也就是两个整数我们当然可以用位操作去修改但是整数都是32个比特位而如果当操作系统想要扩展这两张位图的时候一个整型就放不下了所以操作系统给我们提供了一种用户层的类型这个类型可以直接设置进操作系统的位图里。 而且上面的三张表都属于操作系统的内核数据结构它不允许用户直接修改这三张表所以操作系统必须给我们提供系统调用修改这三张表。我们要获取的 pending 表和 block 表都是位图这就注定了要在用户空间到内核空间内核空间到用户空间进行来回拷贝所以数据拷贝时就要在系统调用接口的参数上设置输入输出型参数。 所以操作系统给我们提供了一种经过封装的数据类型来获取内核中的位图就是 sigset_t. 1sigset_t sigset_t 其实就是一个位图结构我们称为信号集。因此未决和阻塞标志可以用相同的数据类型 sigset_t 来存储sigset_t 称为信号集这个类型可以表示每个信号的“有效”或“无效”状态在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。 sigset_t 类型对于每种信号用一个bit表示“有效”或“无效”状态至于这个类型内部如何存储这些bit则依赖于系统实现从使用者的角度是不必关心的使用者只能调用以下函数来操作 sigset_t 变量而不应该对它的内部数据做任何解释比如用 printf 直接打印 sigset_t 变量是没有意义的。 #include signal.hint sigemptyset(sigset_t *set); // 清空信号集int sigfillset(sigset_t *set); // 将整个位图置1int sigaddset (sigset_t *set, int signo); // 向指定信号集中添加指定信号int sigdelset(sigset_t *set, int signo); // 在指定信号集中去掉指定信号int sigismemberconst sigset_t *set, int signo); // 判断指定信号是否在信号集中2sigprocmask() 调用函数 sigprocmask 可以读取或更改进程的信号屏蔽字(阻塞信号集)接口如下 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);其中第一个参数代表我们需要设置还是获取 block 表有三个选项如下它们之间是并列关系只能三选一 SIG_BLOCKset 包含了我们希望添加到当前信号屏蔽字(block表)的信号相当于 mask mask | setSIG_UNBLOCKset 包含了我们希望从当前信号屏蔽字(block表)中解除阻塞的信号相当于 mask mask ~setSIG_SETMASK设置当前信号屏蔽字(block表)为 set 所指向的值相当于 mask set 第二个参数就是我们当前设置的信号集它是一个输入型参数第三个参数是一个输出型参数当我们对进程的 block 表做修改的时候在改之前系统会将改之前的表通过 oldset 保存起来。那么修改之后然后我们就能通过 oldset 拿到上一次的 block 表。 如果不需要的话可以设为 nullptr. 返回值则是成功返回0失败返回-1. 3sigpending() 读取当前进程的未决信号集通过 set 参数传出。调用成功则返回0出错则返回-1接口如下 int sigpending(sigset_t *set);对于 sigpending 函数的唯一参数它是一个输出型参数就是调用进程所对应的 pending 表带出来。 4signal() signal() 接口我们早就接触过了它就是用来修改 handler 表的接口如下 typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);5测试系统接口 下面我们写一个这样的代码先将2号信号阻塞然后打印出 pending 表我们初始化的时候应该是全0的然后我们给进程发送2号信号因为2号信号被阻塞了所以 pending 表中2号信号所对应的比特位在没有被解除阻塞前一直都是1的然后我们打印 pending 表出来观察是否如此。代码如下 void Print(sigset_t sigset){for(int i 31; i 1; i--){if(sigismember(sigset, i)){cout 1;}else{cout 0;}}cout pid: getpid() endl;}int main(){sigset_t sigset;sigemptyset(sigset); // 信号集清0sigaddset(sigset, 2); // 将2号信号的比特位设置为1sigset_t oldsigset;// 1. 屏蔽2号信号sigprocmask(SIG_SETMASK, sigset, oldsigset);while(1){// 重复打印当前进程的 pending 表int n sigpending(sigset);if(n 0) break;Print(sigset);sleep(1);// 发送2号信号... kill -2 pid}return 0;}结果如下 注意9号和19号信号也是不可以被阻塞的 二、信号捕捉处理 1. 信号的处理 我们在上面说过信号保存是为了让进程在合适的时候处理那么信号是什么时候被处理的呢首先进程要处理一个信号前提是要知道自己收到信号了就必须得合适的时候去查 pending表、block表和 handler表而它们都属于内核数据结构而这说明进程必须处于内核状态才能对信号做处理所以结论就是当进程从内核态返回到用户态的时候进行信号的检测和处理 那么用户态什么时候才会陷入内核态呢一般最常见的就是调用系统调用的时候这时候操作系统是自动会做“身份切换”的用户身份变成内核身份或者反过来。 2. 理解用户态和内核态 下面我们开始理解用户态和内核态这时候我们又要回到我们学习过的地址空间了我们知道每个进程PCB都有自己的地址空间而我们以前也讲过0~3GB 的空间为用户空间3~4GB 为内核空间如下图 下面我们正式介绍一下内核空间其实内核空间中映射的就是操作系统的代码和数据。由于操作系统是被计算机最先加载的软件所以一般操作系统被加载的时候它的代码和数据是被加载到靠内存的底侧的位置那么内核空间怎么和操作系统的代码和数据建立映射呢没错它们之间还有一个内核级的页表但是其实内核空间可以直接和物理内存建立映射它就是固定的偏移量用其中的地址减去3GB就可以得到但是我们先不谈这种方式。 也就是说用户有用户级的页表映射到物理内存中内核有自己的内核级页表映射到操作系统的代码和数据。那么当系统中有许多进程的话有几个进程就有几份页表因为进程之间具有独立性但是内核级页表只有一份所以所有进程的 3~4GB 的内核空间和内核级页表还有映射的操作系统的代码和数据都是一样的也就是说在整个系统中进程再怎么切换3~4GB 的空间内容是不变的 所以站在进程角度所有的系统调用都在内核空间中被进程所看到所以每一个进程在调用系统调用时在代码区调用就可以相当于在自己的地址空间里面调用该方法调用完成之后再返回自己的代码区中就如同在自己的地址空间里直接调用 那么站在操作系统角度任何一个时刻都有进程在执行。因为操作系统在我们开机的时候已经启动了说明操作系统本身也是一个进程那么它也要自己的地址空间它甚至可以不要用户的空间只要自己的内核空间。那么只要有进程在执行我们想执行操作系统的代码就可以随时执行 其实操作系统的本质就是基于时钟中断的一个死循环。在计算机硬件中有一个时钟芯片在每一个非常短的时间内会向CPU发送时钟中断而CPU接收到了中断就要执行该中断所对应的方法这个中断所对应的方法就是操作系统的代码相当于这个时钟中断在推动操作系统在运行 那么我们再回到地址空间中我们以前在进程中调用自己的方法或者代码都是在用户区调的但是当我们需要调用操作系统的代码并不是我们想调就调的因为用户无法直接访问操作系统那么我们就要再介绍CPU了在CPU中有一个叫做CR3的寄存器这个寄存器直接指向的就是当前进程所对应的用户级页表当进程被调度的时候该进程的用户级页表的地址就会被放在这个寄存器中这里保存的是物理地址。 我们怎么知道当前访问的是用户态还是内核态呢那么在CPU中还有一个寄存器叫做ecs寄存器当我们执行用户态的代码的时候ecs寄存器一定指向的是用户态的代码如果我们切换到了内核它就会指向内核中的代码而在ecs寄存器里它的最低两个比特位记录的是CPU的工作模式其中CPU常见的工作模式有用户态和内核态我们知道两个比特位一共就四种状态那么它内部就是用 00(0) 和 11(3) 分别代表内核态和用户态。也就是说如果我们想访问内核中的代码我们必须要将ecs寄存器中的低两位由 11 设为 00就变成内核态了我们就可以访问操作系统的数据了那么谁能修改ecs寄存器呢所以CPU必须给我们提供一个方法能够改变CPU的工作级别于是就有了 int 80这其实是一个汇编语句意思就是陷入内核 所以我们就能理解什么是用户态什么是内核态了。其中 内核态允许访问操作系统的代码和数据用户态只能访问用户自己的代码和数据 3. 信号的捕捉 我们理解了内核态和用户态之后我们下面结合下图来理解信号的捕捉 所以信号保存是为了让进程在合适的时候处理那么信号是在内核态返回用户态时进行处理的 4. 系统调用 1sigaction() 我们前面已经知道信号捕捉可以使用 signal()那么除了 signal() 之外还有一个系统调用接口 int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);sigaction() 的功能也是捕捉特定信号和 signal() 功能一模一样。它的第一个参数是信号的编号第二个参数和第三个参数的类型是一样的都是 struct sigaction*而第二个参数是输入型参数它是把我们用户设置的自定义捕捉方法以及其它信息通过 act 传递给操作系统第三个参数 oldact 就是输出型参数就是将旧的方法保存给我们传递出来。 下面我们看一下 struct sigaction 的结构体 struct sigaction {void (*sa_handler)(int);void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void);};以上的五个字段中我们只需要知道第一个字段 sa_handler 和第三个字段 sa_mask 即可其它都是与实时信号有关的字段我们不用关心。 例如我们使用如下代码进行测试 void myhandler(int signo){cout ...get a signal: signo endl;}int main(){struct sigaction act, oact;memset(act, 0, sizeof(act));memset(oact, 0, sizeof(oact));act.sa_handler myhandler;sigaction(2, act, oact);while(1){cout i am a process, pid: getpid() endl;sleep(1);}return 0;}2pending 表的置0顺序 下面我们说一下当进程收到一个信号pending 位图对应的位置变成1那么它是在执行对应方法前由1置0还是在执行对应方法后由1置0呢 我们可以在执行捕捉方法时打印 pending 表观察 pending 表在执行捕捉方法时对应的位置是否已经置0如果已经置0说明是在执行捕捉方法前由1置0否则相反下面我们验证一下 void PrintPending(){sigset_t sigset;sigpending(sigset);for(int signo 31; signo 1; signo--){if(sigismember(sigset, signo))cout 1;elsecout 0;}cout , ;}void myhandler(int signo){PrintPending();cout ...get a signal: signo endl;}int main(){struct sigaction act, oact;memset(act, 0, sizeof(act));memset(oact, 0, sizeof(oact));act.sa_handler myhandler;sigaction(2, act, oact);while(1){cout i am a process, pid: getpid() endl;sleep(1);}return 0;}结果如下 如上说明一旦收到信号时 pending 表的对应位置置一旦进行信号递达时先将 pending 表的对应位置由1置0再执行方法。 3struct sigaction 中的 sa_mask 字段 当某个信号的处理函数被调用时内核自动将当前信号加入进程的信号屏蔽字当信号处理函数返回时自动恢复原来的信号屏蔽字这样就保证了在处理某个信号时如果这种信号再次产生那么它会被阻塞到当前信号处理结束为止。也就是说不允许同一个信号不断向一个进程发送使进程不断执行该信号的处理函数。 下面我们也可以验证一下我们只需要将上面代码的自定义处理方法修改一下即可我们在 myhandler 中写个死循环打印 pending表这样就能让2号信号一直在处理了这时候我们再给进程发送2号信号这时候2号信号位置的 pending 表应该变成1如下代码 void myhandler(int signo){cout ...get a signal: signo endl;while(1){PrintPending();sleep(1);}}结果如下 那么 struct sigaction 中的 sa_mask 字段是用来干什么的呢正如我们上面所说如果正在处理2号信号2号信号会被屏蔽那么如果还希望自动屏蔽另外一些信号则用 sa_mask 字段说明这些需要额外屏蔽的信号当信号处理函数返回时会自动恢复原来的信号屏蔽字。 我们在给 sa_mask 字段说明需要屏蔽哪些信号时需要使用 sigaddset 设置信号集然后往 sa_mask 中设置即可例如添加屏蔽3号信号 sigaddset(act.sa_mask, 3);三、信号扩展 1. 可重入函数 当我们进行链表插入时假设插入节点 node1insert 分为两个步骤先连接 next 指针再更新 head 指针那么如果我们在刚刚完成第一步的时候因为硬件中断等原因使进程切换到内核再次回用户态之前检查到有信号待处理于是就去处理该信号而该信号的处理方法又是自定处理方法该方法就是再插入一个节点 node2那么该方法执行完毕后返回用户态此时的 head 指向 node2。然后继续回到插入 node1 的代码中完成剩下的代码最后 head 指向了 node1此时 node2 发生节点丢失内存泄漏如下图 更形象的图如下 像上例这样insert 函数被不同的控制流程调用有可能在第一次调用还没返回时就再次进入该函数这称为重入insert 函数访问一个全局链表有可能因为重入而造成错乱像这样的函数称为不可重入函数反之如果一个函数只访问自己的局部变量或参数则称为可重入(Reentrant) 函数。 2. volatile 该关键字在 C 当中的类型转换我们已经有所涉猎今天我们站在信号的角度重新理解一下。 我们先看以下代码定义一个全局的 flag然后在 main 函数中以 flag 为恒真条件执行死循环最后打印一语句我们知道这种情况下该语句是不会被打印的 int flag 1;int main(){while(flag);cout process quit! endl;return 0;}我们只能通过 ctrl c 发送2号信号终止该进程 但是今天我们可以使用信号捕捉对2号信号自定义方法中将 flag 的值修改为1这样就可以让主程序跳出死循环从而打印该语句了如下 int flag 1;void myhandler(int signo){flag 0;}int main(){signal(2, myhandler);while(flag);cout process quit! endl;return 0;}结果如下 但是如果在优化条件下当编译器检测到我们的 flag 在主程序中并没有被修改的时候flag 变量可能被直接优化到 CPU 内的寄存器中即每次读取 flag 的数据的时候只在 CPU 中读取但是 flag 在内存中也有对应的空间当我们使用信号捕捉修改 flag 的值时只会修改内存中的 flag 的值不会影响 CPU 中的 flag 的值 那么这个优化条件怎么设置呢在 g 下这种优化条件一般是被关闭的需要在编译时加上选项设置那么在 g 中设置这种优化条件的选项为 g -O1其中 O1、O2、O3 都可以我们可以验证一下 如上我们捕捉2号信号将 flag 修改为 0 也无法终止死循环因为此时被优化后是直接从 CPU 中读取 flag 的值了。 那么如果此时我们想关闭这种优化我们就可以在 flag 前加上 volatile 关键字如下 volatile int flag 1;所以加上 volatile 关键字就是为了防止编译器过度优化保持内存的可见性 3. SIGCHLD 信号 我们在进程控制的时候讲过用 wait 和 waitpid 函数清理僵尸进程父进程可以阻塞等待子进程结束也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式父进程阻塞了就不能处理自己的工作了采用第二种方式父进程在处理自己的工作的同时还要记得时不时地轮询一 下程序实现复杂。 其实子进程在终止时会给父进程发 SIGCHLD 信号也就是 17 号信号该信号的默认处理动作是忽略父进程可以自定义 SIGCHLD 信号的处理函数这样父进程只需专心处理自己的工作不必关心子进程了子进程终止时会通知父进程父进程在信号处理函数中调用 wait 清理子进程即可。 但是由于 UNIX 的历史原因要想不产生僵尸进程还有另外一种办法父进程调用 sigaction 或者 signal 将 SIGCHLD 的处理动作置为 SIG_IGN也就是忽略这样 fork 出来的子进程在终止时会自动清理掉不会产生僵尸进程也不会通知父进程。 但是上面不是说该信号的默认处理动作是忽略的吗为什么还要我们自己使用系统接口处理呢其实系统对于17号信号的默认处理动作是 SIG_DFL也就是使用默认处理动作只不过 SIG_DFL 默认执行的动作是忽略而我们自己使用接口设置的 SIG_IGN 就是直接将默认处理动作设置为忽略还记得我们上一节讲的信号的处理方式有三种默认动作、忽略、自定义动作 吗其中 SIG_DFL 就是默认动作SIG_IGN 就是忽略
http://www.pierceye.com/news/160024/

相关文章:

  • 网站后台无编辑器扒人家网站做网站
  • 有什么网站做打印店网站开发计划甘特图
  • 网页模板好的网站好滑县网站建设服务
  • 做网站需要学会些什么建设网银登录官方网站
  • phpcms双语网站怎么做深圳做地铁的公司网站
  • 郑州的电子商城网站建设济南网站建设大标网络
  • 网站建设前端和后端的区别网站建设未来发展
  • 深圳网站制作公司建设网站seo视频狼雨seo教程
  • 建网站做优化重庆世界500强企业
  • 手机网站建设合同拼多多网店
  • 手机网站二级域名网站开发多少钱一个
  • 车险网站模版在线表白网页制作
  • 网站建设寻找可以途径wordpress 调试php代码
  • 济南优化seo网站建设微信公众号?
  • 武夷山网站推广三星网上商城下载
  • wap网站开发文案素材网站
  • 做网站需要用什么系统昆山张浦做网站
  • 钟祥建设局网站网页样式与布局
  • j建设银行信用卡网站天河外贸网站建设
  • 石家庄网站建设招商wordpress漫画主题
  • 河南省建设厅网站查询佛山著名网站建设公司
  • 山东搜点网站建设哪家公司做网站最好
  • 云购物网站建设wordpress离线编辑
  • 有没有网站开发团队郑州网站制作电话
  • 网站怎么做登陆免费虚拟机
  • 中国移动网站备案管理系统不能用科普网站建设的支持力度
  • 谁教我做啊谁会做网站啊企业网站模板seo
  • 自己建立一个网站需要什么wordpress 平衡插件
  • 邯郸手机建站价格青海网站开发 建设
  • 苏州 手机网站免费个人简历模板电子版可填写