修改信号的响应方式
1.signal()方法介绍:
修改信号的响应方式要用到方法signal()。需要引用头文件signal.h。signal()的原型:
typedef重命名了一个函数指针的类型,这个指针的类型为指向一个参数为int返回值为void的函数的指针。这个函数指针作为signal的返回值,也作为为signal的第二个参数,所以可以传入一个函数指针,即传入函数的地址,也就是函数名;signal第一个参数信号的代号,是整数值,所以是int型。signal方法表达的意思就是如果收到一个代号为signum的信号,该做出什么响应,就是用第二个参数handler来设置。
signal方法的第二个参数有三种,分别是默认、忽略和用户自定义:
默认:SIG_DFL,它实际上是把0强转成了一个(void(*)(int))
类型的函数指针。
忽略:SIG_IGN,它实际上是把1强转成了一个(void(*)(int))
类型的函数指针。
用户自定义:自己写的信号处理函数
2.用signal修改SIGINT信号的响应方式
在键盘上按下Ctrl+c时,会给当前终端前台执行的进程发送SIGINT信号
(1)给当前终端前台执行的进程发送SIGINT信号之后默认的响应方式
代码如下:
上述代码,是一个死循环,每一秒会输出一个hello。
编译并运行:
由结果可以看出,执行main程序之后会每隔一秒输出一个"hello",知道输入Ctrl+C之后该程序才会终止,该进程也就结束了。就是因为给当前终端前台执行的进程main发送了SIGINT信号,然后以默认方式响应,将进程结束。
也可以把代码修改为以下形式,编译和运行结果和上面的代码一样:
(2)给当前终端前台执行的进程发送SIGINT信号之后自定义的响应方式
代码如下:
代码分析:
代码顺序执行,上述代码14行的signal(SIGINT,sig_fun);
其实是做了一个约定,这条代码中的signal也不是去调用SIGINT和sig_fun,而是把它两作为参数传给了signal,sig_fun什么时候调用由内核决定,因为sig_fun是一个回调函数,由用户编写但是不由用户来调用。signal()不是在while循环里面调用,而是内核调用,也就是说如果是SIGINT这个信号,内核就会帮忙调用sig_fun这个函数。改变了信号的响应方式,在给进程发送了SIGINT信号之后不再按照默认方式响应退出当前进程,而是去调用sig_fun()
这个函数。当前进程在执行while循环的时候,接收到SIGINT这个信号之后while循环先暂停,内核就会帮忙去调用sig_fun这个函数,然后执行sig_fun这个函数的函数体,最后再恢复while循环的执行。
编译并运行的结果:
(3)给当前终端前台执行的进程发送SIGINT信号之后忽略的响应方式
代码如下:
编译并运行:
由结果可以看出,执行main程序之后,通过给当前终端前台执行的进程main发送SIGINT信号想要使进程结束的时候,并没有成功,是因为把SIGINT信号的响应方式修改成了忽略的方式,进程main将这个信号忽略掉继续执行,然后通过别的信号来终止这个程序。
(4)连续执行signal方法,该进程会以最后一次执行signal方法来响应这个信号
有以下代码:
在while循环中执行两次不同的signal方法,分别是忽略响应信号SIGINT和默认响应信号SIGINT。
编译并运行:
由结果可以看出,给正在执行的进程发送了两个SIGINT信号之后,当前进程会以最后一个SIGINT信号默认的响应方式对SIGINT信号进行响应。对于相同的信号来说,程序中最后的信号响应会把前面的信号响应覆盖掉。
(5)第一次发出SIGINT信号时,当前进程以用户自定义的方式对信号进行响应,第二次发出SIGINT信号时以默认方式对信号进行响应。
代码如下:
代码分析:
代码顺序执行,先执行15行代码signal(SIGINT,sig_fun)
,如果当前进程正在执行的时候发送信号SIGINT,这时会调sig_fun函数对该信号做出响应,打印这个信号的代号,打印完之后回到sig_fun函数中,sig_fun内部又执行了11行代码signal(SIGINT,SIG_DFL);
,再一次向进程发出SIGINT信号时,就是以默认的方式响应该信号。
编译并运行的结果: