lv5 嵌入式开发-10 信号机制(下)

目录

1 信号集、信号的阻塞

2 信号集操作函数

2.1 自定义信号集

2.2 清空信号集

2.3 全部置1

2.4 将一个信号添加到集合中

2.5 将一个信号从集合中移除

2.6 判断一个信号是否在集合中

2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞)

2.8  使进程挂起(暂停执行)直到收到一个信号

2.9 更改进程的信号屏蔽字,并等待一个特定的信号到来


掌握:信号集和信号屏蔽

1 信号集、信号的阻塞

应用:

有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延

时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现。

信号的阻塞概念

信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。

信号的状态:

信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)

信号未决(Pending):从产生到递达之间的状态(挂起)

下图表示了,每一个任务控制块都有一个挂起信号集(可读)和信号屏蔽字(可写可读)。信号屏蔽置1,代表忽略该信号的到来

lv5 嵌入式开发-10 信号机制(下)_第1张图片

2 信号集操作函数

2.1 自定义信号集

sigset_t set; 

自定义信号集。  是一个  64bit  128bit的数组。

2.2 清空信号集

sigemptyset(sigset_t *set);	

代表接收所有信号

2.3 全部置1

sigfillset(sigset_t *set);	

接收全部信号

2.4 将一个信号添加到集合中

sigaddset(sigset_t *set, int signum);	

接收某一个位的信号

2.5 将一个信号从集合中移除

sigdelset(sigset_t *set, int signum);	

屏蔽某一个位的信号

2.6 判断一个信号是否在集合中

sigismember(const sigset_t *set,int signum); 

2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞)

#include 
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );

参数说明:

  • how:表示如何修改信号屏蔽字的标志,可取以下值:
    • SIG_BLOCK:将set中指定的信号添加到当前信号屏蔽字中。
    • SIG_UNBLOCK:将set中指定的信号从当前信号屏蔽字中移除。
    • SIG_SETMASK:用set中指定的信号集替换当前信号屏蔽字。
  • set:指向要修改的新的信号屏蔽字的指针。如果为NULL,则不会修改信号屏蔽字。
  • oset:用于保存旧的信号屏蔽字的指针。如果为NULL,则不会保存旧的信号屏蔽字。

函数返回值:

  • 成功:返回0,并将旧的信号屏蔽字保存到oset中(如果oset不为NULL)。
  • 失败:返回-1,并设置errno来指示错误的原因。

该函数用于在多线程编程、信号处理等场景中控制信号的屏蔽,以保证信号处理的正确性和可靠性。

示例:

#include 
#include 
#include 
#include 


void handle(int sig){
    
    printf("I get sig=%d\n",sig);

}

int main(){
    
    struct sigaction act;
    act.sa_handler = handle;   //注册回调函数
    sigemptyset(&act.sa_mask); //清空信号集,代表接收所有信号
    act.sa_flags = 0;          //通过将其设置为0,表示不使用任何额外的标志位,即默认行为。
    sigaction(SIGINT,&act,NULL); //信号捕获
    sigset_t set;               //定义信号集
    sigemptyset(&set);          //清空信号集
    sigaddset(&set,SIGINT);     //SIGINT,屏蔽SIGINT信号

    sigprocmask(SIG_BLOCK,&set,NULL);  //ctrl+c的信号阻塞,接收不到
    sleep(5);
    sigprocmask(SIG_UNBLOCK,&set,NULL); //取消信号阻塞

    while(1){
        sleep(1);
    }

}

执行效果 :

2.8  使进程挂起(暂停执行)直到收到一个信号

int pause(void);

进程一直阻塞,直到被信号中断,返回值:-1 并设置errno为EINTR

函数行为:

-1 如果信号的默认处理动作是终止进程,则进程终止,pause函数没有机会返回。

-2 如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause函数不返回

-3 如果信号的处理动作是捕捉,则调用完信号处理函数之后,pause返回-1。

-4 pause收到的信号如果被屏蔽,那么pause就不能被唤醒 

示例:pause阻塞效果

#include 
#include 
#include 
#include 


int main(int argc, char * argv[])
{
	pause();
	printf("after pause\n");
}

//运行后结果程序阻塞中,未打印after pause,按ctrl+c或ctrl+\结束
linux@linux:~/Desktop$ ./pause 
                   

 示例:pause阻塞效果2

#include 
#include 
#include 
#include 

void handle(int sig)
{
	printf("Handle:I get sig=%d\n",sig);
}

int main(int argc, char * argv[])
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	
	pause();
	printf("after pause\n");
}

//运行结果,未恢复至原处理函数的执行效果
linux@linux:~/Desktop$ ./pause 
^CHandle:I get sig=2
after pause
linux@linux:~/Desktop$

 示例:pause阻塞效果3 ,实现一个等待信号的任务

#include 
#include 
#include 
#include 

void handle(int sig)
{
	printf("Handle:I get sig=%d\n",sig);
}

void mytask()
{
	printf("mytask:start\n");
	sleep(3);
	printf("mytask:end\n");
}

int main(int argc, char * argv[])
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	
	pause();
	while(1)
	{
		mytask();   
		pause();   //每次等待信号到来再执行mytask
	}
	printf("After pause\n");
}  

//运行结果
linux@linux:~/Desktop$ ./pause 
^CHandle:I get sig=2
mytask:start
mytask:end
^CHandle:I get sig=2
mytask:start
mytask:end
^CHandle:I get sig=2
mytask:start
mytask:end

使用kill 发送SIGHUP信号关闭 

lv5 嵌入式开发-10 信号机制(下)_第2张图片  示例:pause阻塞效果4 ,实现一个等待多个信号的任务(SIGINT,SIGHUP)

#include 
#include 
#include 
#include 

void handle(int sig)
{
	printf("Handle:I get sig=%d\n",sig);
}

void mytask()
{
	printf("mytask:start\n");
	sleep(3);
	printf("mytask:end\n");
}

int main(int argc, char * argv[])
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	sigaction(SIGHUP,&act,NULL);	

	pause();
	while(1)
	{
		mytask();
		pause();
	}
	printf("After pause\n");
}

按ctrl+c,或者使用kill -1发送关闭信号都可以捕获信号处理任务。

lv5 嵌入式开发-10 信号机制(下)_第3张图片

 示例:使用 sigaddset,屏蔽多个信号

#include 
#include 
#include 

void handle(int sig)
{
	printf("Handle:I get sig=%d\n",sig);
}

void mytask()
{
	printf("mytask:start\n");
	sleep(3);
	printf("mytask:end\n");
}

int main(int argc, char * argv[])
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	sigaction(SIGHUP,&act,NULL);
	
	sigset_t set;
	sigaddset(&set,SIGHUP);
	sigaddset(&set,SIGINT);	

	pause();
	while(1)
	{
		sigprocmask(SIG_BLOCK,&set,NULL);
		mytask();
		sigprocmask(SIG_UNBLOCK,&set,NULL);
		pause();
	}
	printf("After pause\n");
}

执行效果,在任务过程中设置了信号屏蔽,此时按ctrl+c或者kill -1去发送信号,不会执行handle打印,直到任务结束后再开启接收信号,防止任务被打断

lv5 嵌入式开发-10 信号机制(下)_第4张图片

2.9 更改进程的信号屏蔽字,并等待一个特定的信号到来

设定对信号集内的信号屏蔽并恢复。

int sigsuspend(const sigset_t *sigmask);

功能:函数用于临时更改进程的信号屏蔽字,并等待一个特定的信号到来,然后恢复原有的信号屏蔽字。和 pause 函数不同的是,它可以临时更改进程的信号屏蔽字,以便等待某个特定信号。

参数:

sigmask:希望屏蔽的信号

示例:解决任务中的信号丢失问题,sigsuspend相当于sigprocmask+pause

#include 
#include 
#include 
#include 

void handle(int sig)
{
	printf("Handle:I get sig=%d\n",sig);
}

void mytask()
{
	printf("mytask:start\n");
	sleep(3);
	printf("mytask:end\n");
}

int main(int argc, char * argv[])
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	sigaction(SIGHUP,&act,NULL);
	
	sigset_t set;
	sigaddset(&set,SIGHUP);
	sigaddset(&set,SIGINT);	

	sigset_t set2;                
	sigemptyset(&set2);         //接收所有信号

	pause();
	while(1)
	{
		sigprocmask(SIG_BLOCK,&set,NULL);
		mytask();
		//sigprocmask(SIG_UNBLOCK,&set,NULL);
		//pause();
		sigsuspend(&set2);
	}
	printf("After pause\n");
}

运行结果

linux@linux:~/Desktop$ ./pause 
^CHandle:I get sig=2         //1次CTRL+C
mytask:start  
mytask:end
^CHandle:I get sig=2         //1次CTRL+C
mytask:start
mytask:end
^CHandle:I get sig=2         //1次CTRL+C
mytask:start
^Cmytask:end                 //任务中途的1次CTRL+C
Handle:I get sig=2           //也被获取到并执行了
mytask:start
mytask:end

3 练习

实现信号回收子进程程序

提示:SIGCHLD 是一个用于处理子进程状态变化的信号。当一个子进程终止或停止时,它的父进程会收到 SIGCHLD 信号。

#include 
#include 
#include 
#include 
#include 

void handle(int sig)
{
	wait(NULL);
	printf("Get sig =%d\n",sig);
}

int main(int argc, char* argv[])
{
	pid_t pid;
	struct sigaction act;

	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);

	pid = fork();
	
	if(pid > 0)
	{
		printf("father process\n");
		sigaction(SIGCHLD,&act,NULL);
	}
	else if(pid == 0)
	{
		printf("childe process\n");
		sleep(5);
		exit(0);
	}
	else
	{
		perror("fork");
		return 0;
	}
	
	while(1)
	{
		sleep(1);
	}

	return 0;
}

运行结果:5s后子进程退出,没有产生僵尸进程。

lv5 嵌入式开发-10 信号机制(下)_第5张图片

你可能感兴趣的:(嵌入式开发,linux)