信号使用以及处理

信号使用以及处理
一、引子
在我们的生活中,时时刻刻的在产生着信号,比如朋友之间昵称的问候,老师学生之间提问的表达等等,都是信号的一种具体体现,那么在Linux应用实现的底下,我们是怎么样去使用和认识它,处理它的呢。

二、信号的概念:
信号是UNIX和Linux系统下响应某些条件而产生的一个事件。接收到该信号的进程会相应地采取一些行动,我们平常用术语【生成】(raise)表示一个信号的产生,术语【捕获】(catch)来表示接收到一个信号。一般一个信号的发生,是由于发生了一些不可避免的错误来发送给相应的进程,使之进行处理。而信号可以产生也可以接收,产生和接收的主体都是进程。
信号在系统中的定义为:
/usr/include/bits/signum.h  

信号的名称是在signum.h中定义的,它们以SIG开头进行定义:也从而看出这些信号在底层是通过宏定义的


上述是信号的一些底层定义,接着我们看一下信号的一些说明:

如图所示:


重点说明上述的信号SIGKILL,它是用来终止进程的,但是最重要的还是这个信号的不允许修改它的响应方式,至于响应方式下面将会进行说明;而上面的SIGINT信号可以通过在shell和终端驱动程序按通常的情况配置的话,可以通过键盘敲入中断字符(通常是Ctrl+c组合键)向前台进程(即当前正在运行的程序)发送这个信号(即SIGINT信号),这将引起该程序的终止,除非它事先安排了捕获这个信号。

如果进程接收到这些信号的中的一个,但事先没有安排捕获它,进程将会立即终止。通常,系统将生成核心转储文件core,并将其放在当前目录下,该文件是进程在内存中的映像,它对程序的调试很有用处。


所谓核心转储文件是内含进程终止时内存映像的一个文件。

产生条件:特定的信号会引发进程创建一个核心转储文件并终止运行。


(重点)SIGCHLD信号对于管理子进程很有用,为什么这么说呢,因为子进程结束时会自动向其父进程发送 SIGCHLD 信号,这样可以避免产生僵尸进程,前面博客已经说了僵尸进程的危害和产生僵尸进程的原因了,从而看出SIGCHLD这个信号的“强大之处”。默认情况下,它是被忽略的,其余的信号会使接收它们的进程停止运行,但SIGCONT是个例外,它的作用是让进程恢复并继续运行。shell脚本通过它来控制作业,但用户程序很少使用它。


三、信号的使用以及处理

说之前首先提出:

1、 信号是怎样发送的 2、 信号是怎样接受的 3、 信号接收以后做了什么事情

对于信号的处理我们是用signal这个库函数实现的,它的定义如下:
#include
typedef void (*Fun_Handle)(int);

Fun_Handle signal(int signum, Fun_Handle fun);  

Linux定义如下:借助命令:    man   signal.h      可以查看signal函数的定义。

这个函数的定义说明,signal函数是一个带有整型的signum和一个类型为函数指针的fun共两个参数组成的函数。准备捕获或忽略的信号由signum给出,接收到的信号后要将调用的函数由参数fun给出,信号处理函数(fun)必须要有一个int类型的参数,即接收到的信号代码,并且返回类型为void,signal函数本身也会返回一个同类型的函数,即先前用来处理这个信号的函数。

这里我们说一下除了上述的自定义函数fun还有两个。而第二个参数相当于说成捕获吧。它是信号的一种响应方式。那么信号的响应方式分为哪一种呢

信号的响应方式:
分为三种:     
默认行为 : SIG_DFL
忽略信号 : SIG_IGN
自定义信号: 自定义函数

signal 函数功能: 完成 signum 参数执行的信号与 fun 函数绑定, 当接收到 signum 这个信号, 执行 fun 这个函数。  

signal函数出差错的特殊处理及说明:signal函数返回的是先前对指定信号进行处理的信号处理函数的指针,如未定义信号处理函数,则返回SIG_ERR并设置errno 为一个正数值,如给出一个无效的信号,或者尝试处理的信号是不可捕获或者不可忽略的信号(如SIGKILL),errno将设置为EINVAL;

总体而言信号的发送是通过函数signal中参数函数指针,指向fun这个函数,而信号处理函数fun对signal函数第一个参数signum传递进来的信号作出响应,信号出现时,信号调用该函数(fun函数,也就是signal函数的第二个参数函数指针所指向的函数),实现fun函数里面的功能,下面我们将举例说明:

编程题一:
编写程序实现:当进程第一次接收到 SIGINT 信号后,打印“i  got  signal  ”!  ,并且通过fun()函数打印signal第一个参数signum信号的值;

实现代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

void fun(int sig)
{
	printf("i got signal %d\n",sig);
}

void main()
{
	signal(SIGINT,fun);
	while (1)
	{
		printf("i am go to main\n");
		sleep(1);
	}
}

结果如下:


分析:通过上面实例发现,它将响应ctrl+c组合键,也就是信号SIGINT信号,当fun这个函数处理信号SIGINT的时候,打印信号SIGINT的值和一串字符"i  got   signal" ,函数fun对参数signum传递进来的信号做出响应,信号出现时,程序调用该函数,fun函数执行完打印后,然后将信号SIGINT(默认情况下,按下CTRL+C将产生这个信号)的处理方式恢复为默认行为。main()函数的作用是,截获按下CTRL+C组合键产生的SIGINT信号,没有信号时,它会子啊一个无限循环中每隔一秒打印一条信息。每一次按下CTRL+C组合键会让程序做出响应,然后程序继续执行。(重点)从而说明signal这个函数是由底层操作系统内核实现完成的,它与main函数是各自执行自己的互不干扰。

看下图signal在帮助手册定义:


(重点):左上角(右上角)signal(2)里面的2就说明这signal函数是由系统内核完成的,只供给用户使用的时候一个用户接口。下面是signal的帮助手册定义。并且在调用signal函数时,它的第二个参数:函数指针,不是在调用,而是相当于一个函数地址,把这个地址传给了signal函数,供它使用,函数调用是signal()  ,而我们上面所写明显没有()从而说明它是一个函数地址(函数指针)。

编程题二:
编写程序实现:当进程第一次接收到 SIGINT 信号后,打印“sig =  ”,主函数打印hello!第二次接收到SIGINT 信号后, 结束进程。  

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <signal.h>


void fun(int sig)
{
	printf("sig = %d]\n",sig);
	signal(SIGINT,SIG_DFL);

}

int main(int argc,char *argv,char *envp)
{
	signal(SIGINT,fun);
	while(1)
	{
		printf("hello\n");
		sleep(1);
	}
}

结果如下:


分析:第一次按下CTRL+C 组合键会让程序做出响应,程序会在中断函数fun()处理完毕然后程序继续运行,再次按下CTRL +C的时候,程序将终止,也就是程序将结束运行,因为SIGINT信号的处理方式已恢复默认行为---终止程序的运行,sig = 2 就是SIGINT信号在底层定义宏的值,从而说明信号处理函数fun(int sig)默认参数为int  sigum ,且信号处理函数使用了一个单独的整形参数,它就是引起该函数调用的信号代码。如果需要在同一个函数中处理多个函数,这个参数就很有用,在这个例子中,我们打印了SIGINT的值,它的值在系统中定义是数字2,但不要过于依赖传统的信号数字值,而应该在新的程序中总是使用信号的名字。
       
如果想保留信号处理函数,让它继续响应用户的CTRL+C 组合键,我们就要再次调用signal函数来重新建立它,这会使信号在一段时间内无法得到处理,这段时间从调用中断函数开始,到信号处理函数的重建为止,如果在这段时间内程序接收到第二个信号,它会违背我们的意愿将程序终止程序的运行。

如果第一次接收到信号, 进入信号处理函数, 执行 10 秒钟, 主程序不会同时执行, 主程序会等待信号处理函数执行完成后, 接着运行。
如果信号处理函数正在执行, 信号被再次触发, 则第二次触发的信号会被放到信号的等待队列中, 直到之前的信号处理函数执行完成, 才会被处理。  

正确处理常识: 在信号处理函数中,调用如printf这样的函数是不安全的,一个有用的技巧是,在信号的处理函数中设置一个标志,然后再主程序中检查该标志,如需要就打印一条信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值