signal函数,调用捕获函数的过程(信号)【linux】(zr)

signal函数

函数原型

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

功能
设置某个信号的处理方式。
处理方式可以被设置为忽略,捕获,默认
进程的进程表(task_struct)中会有一个“信号处理方式登记表”,专门用于记录信号的处理方式,调用signal函数设置某个信号的处理方式时,会将信号的处理方式登记到该表中。

每个进程拥有独立的task_struct结构体变量,因而每个进程的“信号处理方式登记表”都是独立的,所以每个进程对信号的处理方式自然也是独立的,互不干扰。

参数
1)signum:信号编号。
2)handler:信号处理方式。
sighandler_t是被typedef后的类型,原类型 void (*)(int),这是一个函数指针类型。

sighandler_t handler也有直接写成void (*handler)(int)。
sighandler_t signal(int signum, void (*handler)(int));

(a)忽略:SIG_IGN
(b)默认:SIG_DFL
(c)捕获:填写类型为void (*)(int)的捕获函数的地址,当信号发生时,会自动调用捕获函数来进行相应的处理。

捕获函数需要我们自己来实现,捕获函数的int参数,用于接收信号编号。

捕获函数也被称为信号处理函数。

void signal_fun1(int signo)
{
...
}
							
void signal_fun2(int signo)
{
...
}
					
int main(void)
{
signal(SIGINT, signal_fun1);
signal(SIGSEGV, signal_fun2);
return 0;
}

捕获函数什么时候被调用?
进程接收到信号时就调用,调用时会中断进程的正常运行,当调用完毕后再会返回进程的正常运行。

返回值
成功:返回上一次的处理方式
失败:返回SIG_ERR宏值,并且设置errno。

代码演示:
重新设置SIGINT和SIGQUIT信号的处理方式:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>





void signal_fun(int signo)
{
	
	if(signo == SIGINT)
	{
		printf("SIGINT signo=%d\n", signo);
	}
	else if(signo == SIGQUIT)
	{
		printf("SIGQUIT signo=%d\n", signo);
	}

}




int main(void)
{
signal(SIGINT, SIG_IGN);  //将SIGINT信号设置为忽略
signal(SIGINT, SIG_DFL);   //将SIGINT信号还原为系统默认的处理方式
signal(SIGINT, signal_fun);
//将SIGINT信号设置为捕获,signal_fun就是捕获函数的地址,
//SIGINT信号在捕获函数中进行处理
	signal(SIGINT, SIG_DFL);

signal(SIGQUIT, SIG_IGN);     //将SIGQUIT信号设置为忽略signal(SIGQUIT, SIG_DFL);     //将SIGQUIT信号还原为系统默认的处理方式
signal(SIGQUIT, signal_fun);   
//将SIGQUIT信号设置为捕获,signal_fun就是捕获函数的地址
//SIGQUIT信号在捕获函数中进行处理

	while(1);

	return 0;
}

调用捕获函数的过程

当信号没有发生时,进程正常运行,当信号发生时,进程的正常运行会被中断,然后去处理信号,一看信号的处理方式是捕获,就会从“信号处理方式登记表”中将捕获函数的地址取出并执行捕获函数,捕获函数执行完毕后,恢复进程的正常运行。

不过当信号来时,如果当前有一条指令正在运行,会先等这条指令运行执行完毕后再去调用信号处理函数。

如果捕获函数有调用exit或者_exit的话,进程会被终止,不过是正常终止。

如果信号处理函数有提前执行return的话,会提前返回到主线。

值得强调的地方

1)信号被设置为SIG_DFL时,表示将处理方式设置为默认
其实在不做任何处理方式设置的情况下,信号的处理方式就是系统设置的默认处理方式。

(2)信号被设置为SIG_IGN(忽略)时进程将不会再接收到这个信号,这信号对进城没有任何影响。

(3)设置为捕获时,需要将handler设置为捕获函数的地址,类型为void (*)(int)

为了确保和捕获函数的类型统一,SIG_DFL、SIG_IGN和SIG_ERR宏的类型也必须是void (*)(int)。

#define SIG_DFL	((void (*)(int))0)	
#define SIG_IGN	((void (*)(int))1)
#define SIG_ERR	((void (*)(int))-1)

验证这些值。
这几个宏定义在了<signal.h>头文件中。

(4)除了SIGKILL这两个信号外,其它所有的信号都可被忽略和捕获。
之所以不能忽略的原因,就是怕你把所有的信号都给忽略后,当你的程序跑飞后,除了重启机器外,你还就真没有办法终止跑飞的程序了,所以Linux规定SIGKILL这两个一定不能被忽略和捕获,至少还有一个保底操作。

(5)在windows下结束进程是怎么回事
在任务管理器里面结束任务,其实就是向进程发送一个信号,进程收到这个信号后,就会被信号终止掉。这一点其实与Linux是一样的。

发布了163 篇原创文章 · 获赞 94 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43648751/article/details/104617271