第12章 处理信号

来源:互联网 发布:新版淘宝怎么看足迹 编辑:程序博客网 时间:2024/06/05 09:01

信号就像一个软中断,当有一些重要的事情发生时,它们就会被发送到程序中。

本章要学习的知识点:

(1)在系统中如何表达信号;

(2)如何查看系统中所有的信号名和对应的数字;

(3)shell编程中常用到哪些信号;

(4)如何发送特定的信号;

(5)如何处理收到的信号;

(6)如何忽略某些信号及如何恢复它们;

(7)如何在脚本退出时做某些特定的处理;

1.     如何表达信号

信号实际上就是一个进程发送给另一个进程的消息,实现进程互通。

 现在Linux系统中有几十种信号的类型,每一个信号都被分配了一个数字和一个名字。在使用时,既可以用数字,也可以用名字。在shell命令行中,可以通过trap-l或kill -l来查看系统中所有的信号。

 通过使用bash的内建命令kill可以发送任意指定的信号到某个进程。如果没有指定参数,kill命令默认发送信号15SIGTERM,在接收这个信号后,进程会终止执行。

    kill命令的格式如下:

       Kill [ -s sigspec | -n signum | -sigspec ] [pid | jobspec ]…

 通过sigspec或signum指定要发送的信号,sigspec既可以是任意大小写的信号名(有无SIG前缀都可以),也可以是给信号分配的数字,而signum只能是数字。kill命令会把指定的信号发送给进程号为pid的进程,或者发送给作业号为jobspec的进程。

 重要提示:

     (1)Ctrl+C等组合键只能给在当前终端中运行的前台进程发送信号,对于其他终端的进程或后台进程是不能发送信号的。

     (2)有时通过kill命令发送信号给某些进程需要root权限,接收进程才会执行希望的操作,此时要通过sudo命令执行kill发送信号。

2.     如何处理信号

在执行过程中,当用户按下组合键Ctrl+C或者使用kill命令发送TERM信号终止脚本的运行,那么临时文件就不会删除,这些残留文件会占用大量的硬盘空间,同时会给用户带来困扰。

 我们可以让运行的脚本在接收TERM信号时,自动地删除这些临时文件,因此需要实现一个自定义回归函数来执行这些操作,而注册TERM信号的这个回调函数就要通过trap命令来实现。

    trap的格式如下:

    trap cmd signa1 signal2…

其中,cmd可以是一系列命令或一个函数的名字,在它后面是一系列信号的数字或信号名。若cmd参数没有指定,shell脚本在接收到这些信号时会执行它们的默认行为。

案例1 实现以上功能的代码如下:

#!/bin/bash


#使用当前脚本运行的进程PID创建一个唯一的文件
TMPFILE=tmpfile.$$


#定义收到信号时回调函数
CleanUp()
{
if[ -f "$TMPFILE" ]
then
   echo
   echo "Cleaning Up..."


   #清除脚本产生的临时文件
   rm -f $TMPFILE 2 > /dev/null
   echo "Done."
   echo
fi


#结束脚本程序的执行
exit 2
}


#注册新号回调函数CleanUp
trap CleanUp 1 2 3 15


#创建临时文件
echo
echo "Creating temporary file $TMPFILE ..."
echo 'date' > $TMPFILE
echo


#模拟一些工作
echo "Script is running ..."
echo -n "       "


#打印进度条
TIME=15
until [ "$TIME" -eq 0 ]
do
echo -n "###"
#每打印一次#号就睡眠一秒
sleep 1
#让TIME的值减一,最终等于零时循环结束
let TIME-=1
done


#执行到这里时,会清除临时文件
echo
echo "Cleaning Up termporary file $TIMPFILE ..."
rm -f $TMPFILE 2 > /dev/null
echo


exit 0


案例2 在案例1是多个信号共用一个信号处理函数,若希望为信号HUP实现一个单独的处理函数,用来重现初始化脚本,该如何实现?

#!/bin/bash


#使用当前脚本运行的进程PID创建一个唯一的文件
TMPFILE=tmpfile.$$


#定义回调函数
Init()
{
echo
echo "Receive SIGHUP,Reinitialize script..."
echo "Deleting temporary file,Creating new one..."
rm -f $TMPFILE 2 > /dev/null
echo 'date' > $TMPFILE
echo "Done."
echo -n "     "
TIME=15
}


#定义收到信号时回调函数
CleanUp()
{
if[ -f "$TMPFILE" ]
then
   echo
   echo "Cleaning Up..."


   #清除脚本产生的临时文件
   rm -f $TMPFILE 2 > /dev/null
   echo "Done."
   echo
fi


#结束脚本程序的执行
exit 2
}


#注册新号回调函数CleanUp
trap CleanUp 2 3 15


#为信号1注册回调函数Init
trap Init 1


#创建临时文件
echo
echo "Creating temporary file $TMPFILE ..."
echo 'date' > $TMPFILE
echo


#模拟一些工作
echo "Script is running ..."
echo -n "       "


#打印进度条
TIME=15
until [ "$TIME" -eq 0 ]
do
echo -n "###"
#每打印一次#号就睡眠一秒
sleep 1
#让TIME的值减一,最终等于零时循环结束
let TIME-=1
done


#执行到这里时,会清除临时文件
echo
echo "Cleaning Up termporary file $TIMPFILE ..."
rm -f $TMPFILE 2 > /dev/null
echo


exit 0

案例3 是否可以在Shell脚本退出时,得到一个信号,从而处理某些特定的操作?

#!/bin/bash


#这个回调函数会在脚本退出以前被调用
quit_handler()
{
echo
echo "In quit_handler():"
echo "Script will exit."
echo "Bye"
echo
}


#注册伪信号0点回调函数,使得在脚本退出以前可以做一些事情,使用EXIT和0的效果是一样的
trap quit_handler 0


echo "These lines prints before the \"trap\"--"
echo "Even thought the Script see the \"trap\" first."
echo


#即使注释exit命令,伪信号0也会产生
exit 0

3.     忽略信号

当执行一个需要较长时间才能完成的脚本时,用户可以通过组合键Ctrl+C来终止脚本的执行,但是如果用户的脚本在执行一些关键性的操作,不希望被打断,如备份或恢复瓷盘数据,就需要屏蔽用户使用Ctrl+C组合键或使用kill命令发送的INT(interrupt)信号。

解决方案:只要把trap命令中的操作指定为空,就可以替换默认的退出操作,从而忽略INT信号。

#使用空字符串货冒号屏蔽信号2 SIGINT

trap ‘’ INT

#冒号是bash的一个内建命令,它总是什么都不做并且总是返回0。

4.     定时器

案例1

如果希望用户在指定的时间内输入用户名,否则脚本就退出执行,该如何实现?

#!/bin/bash


#定义函数Expire_Handler用来捕捉SIGALARM信号
Expire_Handler()
{
echo 
echo "Got SIGALARM signal,Waiting for Your info too long!"
echo "Bye."


#从脚本中以退出代码14退出,用来表示接收到SIGALRM信号
exit 14
}


#定义函数,用来设置一个定时器
Start_Timer()
{
#如果没有指定参数,默认为10秒
local INTERVAL=${1:-10}


#检查参数大于0
if [ $INTERVAL -gt 0 ]
then
   #15秒以后发送信号SIGALRM到脚本进程本身
   sleep $INTERVAL && kill -s 14 $$ &


   #记住后台进程PID,用来杀死定时器
   #如果用户在最后期限以前输入信息
   TIMERPID=$!


else 
echo "Error:Interval must be positive integer!"
exit 1
fi


}


#定义函数,用来杀死后台进程,从而去除定时器
Unset_Timer()
{
#首先杀死子进程
kill 'pgrep -P $TIMERPID'


#然后杀死父进程
kill $TIMERPID
}


#设置定时器回调函数Expire_Handler来捕捉SIGALARM信号
trap Expire_Handler 14


echo 
echo "You have only 15 seconds to enter your info!"
echo


#把定时器设置为15秒到时间
Start_Timer 15


read -p "Please Enter your ID:" ID


#如果用户在最后期限以前输入了信息,则移除定时器
Unset_Timer


echo
echo "Your ID is: $id"
echo "All Done."
echo


exit 0










5.     小结

信号也是进程间通信的一种方式,通过信号可以告诉程序某一个重要的时间正在发生。在脚本的执行过程中,由于信号可以在任何时候到达,会给原来的脚本增加一些相应的复杂性,所以在脚本中需要考虑可能接收到的信号,并添加处理这些信号的方法。

本章介绍了有关信号的内容,包括信号的概念,Shell编程中一些经常用到的信号,如何获得当前平台所支持的信号,发送信号和处理信号的方法,以及一些信号的应用。

本章的重要知识点包括:

(1)信号的两种表达方式,数字和名字;

(2)如何查看所有的信号名和对应的数字;

(3)发送信号的方法;

(4)如何定义信号处理的函数;

(5)进程在收到一个信号时的三种处理信号的方式;

(6)忽略信号和重置信号处理函数的方法;

(7)信号的一些常用应用,如删除临时文件和设置定时器。

0 0
原创粉丝点击