信号驱动任务执行(pause、sigsuspend函数)
创始人
2024-03-18 23:18:53
0

信号驱动任务指的是通过信号来驱动任务的执行,每发送一次信号,任务就执行一次。实现该目的所需的函数就是 pause 或者 sigsuspend,pause和sigsuspend函数可以暂停当前进程,直至收到信号才会继续运行之后的程序。


目录

1、认识 pause / sigsuspend 函数

(1) pause 函数

(2) sigsuspend 函数

2、信号驱动任务执行的两种方式

(1) sigprocmask + pause

(2) sigsuspend


1、认识 pause / sigsuspend 函数

(1) pause 函数

pause 函数的作用是暂停当前进程(进入休眠状态),直至收到信号(任意信号),才会唤醒当前进程。

因为信号的处理动作有终止、忽略、捕捉、屏蔽,所以也对应了下面四种情况:

  • 信号的默认处理动作是终止,进程直接终止。
  • 信号的默认处理动作是忽略,进程继续处于挂起状态。
  • 信号的默认处理动作是捕捉,进程先调用信号处理函数,然后解除挂起,执行下一步。
  • 信号的默认处理动作是屏蔽,进程继续处于挂起状态。

(2) sigsuspend 函数

sigsuspend函数的作用也是暂停当前进程直至收到信号,但 sigsuspend 还可以主动设置屏蔽哪些信号,收到这些被屏蔽的信号时,不会解除挂起。

参数 mask:需要屏蔽的信号集。当前进程的屏蔽字会被替换为当前参数的屏蔽信号集,等到函数返回,会还原当前进程的屏蔽字。

  • mask == NULL:sigsuspend达到的效果和pause一样
  • mask != NULL:通过设置屏蔽信号集来主动屏蔽某些信号,收到这些被屏蔽的信号时,不会解除挂起

返回值:只会返回 -1。

2、信号驱动任务执行的两种方式

下面就以 2号信号为例,实现信号驱动任务执行。2号信号 SIGINT 可以终止前台进程,我们需要捕获2号信号,并设置相应的信号处理函数来避免当前进程被终止。

(1) sigprocmask + pause

执行任务的时候,我们不希望任务执行到一半被打断,所以在执行任务的之前我们需要调用 sigprocmask函数来屏蔽未来会收到的信号(当前场景下指的就是2号)

#include 
#include 
#include void handler(int signum)
{printf("收到并处理%d号信号\n", signum);
}int main(){signal(2, handler);    // 捕捉2号信号sigset_t set;sigemptyset(&set); 		// 清空信号集sigaddset(&set, 2);    // 向信号集中添加2号信号while(1){sigprocmask(SIG_BLOCK, &set, NULL); 	// 屏蔽2号信号printf("------------------\n");printf("task is running\n");printf("------------------\n");sleep(1);sigprocmask(SIG_UNBLOCK, &set, NULL);    // 解除2号信号的屏蔽pause();}return 0;
}

正常情况应该是,解除2号信号的屏蔽以后,pause函数挂起的时候捕捉到了信号,此时会先去调用信号处理函数,然后解除挂起就会重新去打印“task is running” 。然而,从下面的结果可以看出,只执行了信号处理函数,但是pause函数没有解除挂起。

原因就是,由于发送信号较为频繁,在打印“task is running” 的时候收到了2号信号,但是此时2号信号被屏蔽了,解除2号信号的屏蔽以后,立马就去调用信号处理函数了,等到pause函数挂起以后,信号就已经处理完了。

因此,当信号发送较为频繁的时候,不建议使用 pause函数 来驱动任务执行。

(2) sigsuspend

sigsuspend 函数被调用以后,直接进入阻塞等待的状态

  • 如果收到了屏蔽信号集中的信号,不予理会,继续挂起
  • 如果收到了屏蔽信号集以外的信号,解除挂起,并调用对应的信号处理函数 或者 执行默认处理行为。

假设 sigsuspend 函数的屏蔽信号集中包含了3号信号,那么sigsuspend收到3号信号就不会解除挂起;如果收到了2号信号,那么sigsuspend函数就会解除挂起,并调用对应的信号处理函数。

#include 
#include 
#include void handler(int signum)
{printf("收到并处理%d号信号\n", signum);
}int main(){signal(2, handler);    // 捕捉2号信号sigset_t set;sigemptyset(&set); 		// 清空信号集sigaddset(&set, 2);    // 向信号集中添加2号信号sigset_t mask;        // sigsuspend的屏蔽信号集sigemptyset(&mask);sigaddset(&mask, 3);  // 向屏蔽信号集添加3号信号while(1){sigprocmask(SIG_BLOCK, &set, NULL); 	// 屏蔽2号信号printf("------------------\n");printf("task is running\n");printf("------------------\n");sleep(1);sigsuspend(&mask);    // 挂起状态:将进程屏蔽字替换为屏蔽信号集mask// 解除挂起:恢复原本的进程屏蔽字 }return 0;
}

sigsuspend之所以不会像pause那样,是因为sigsuspend函数解除挂起以后的动作是原子的,解除挂起以后,再调用信号处理函数,保证了下一次任务的执行;pause采用的方式是先解除屏蔽,再解除挂起,这就存在一个问题,如果信号在解除挂起之前就被处理了,那么pause函数根本就收不到信号。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...