信号驱动任务执行(pause、sigsuspend函数)

信号驱动任务指的是通过信号来驱动任务的执行,每发送一次信号,任务就执行一次。实现该目的所需的函数就是 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 <stdio.h>
#include <signal.h>
#include <unistd.h>

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 <stdio.h>
#include <signal.h>
#include <unistd.h>

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函数根本就收不到信号。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值