shell中信号处理
名称 值 描述
SIGHUP 控制终端发现被挂起或控制进程死亡
SIGINT 键盘终端
SIGQUIT 来自键盘的退出信号
SIGKILL 杀死进程的信号
SIGALRM 定时时钟中断
SIGTERM 终止信号
发送信号:kill -signal pid
SIGTERM: kill pid 等价于 kill -s SIGTERM pid
如: kill -s SIGHUP 1001; 等价于 kill -1 1001,一个使用信号名称,一个使用代表该信号的整数
SIGKILL: SIGKILL 有不能被捕捉的特殊能力,任何接受到它的进程都要立即终止, kill -9 1001
Shell 脚本可以通过内置的 trap 命令定制自己的信号处理函数,即捕捉发送到当前进程的信号并执行自己定义的函数,而不是执行系统默认的函数。注意,SIGKILL 不可捕捉,为什么呢?因为系统总要留下一种结束进程的手段,否则如果全部信号都被捕获但不即出,那么这个进程就无法被杀掉了。
trap 命令格式如下:
trap 'command_list' signals
其中,command_list 是一个命令清单,可以包含一个函数,在接收到信号列表中包含的某个信号后运行。而 signals 是将要捕捉的信号的列表。
例如:
捕获 SIGINT, 输出一条消息后退出,使用
trap 'echo you hit Ctrl-C\, now exiting..; exit' SIGINT
捕获 SIGINT, 什么也不做,即忽略该信号,使用
trap '' SIGINT
重置 SIGINT,即使用系统默认的信号处理函数
trap - SIGINT
本例中,可以捕获 SIGTERM 信号,并指定信号处理函数来实现资源的清理工作,资源清理干净后再退出。
实现
horen@heart$~> cat signal.sh
#!/bin/bash trap 'TaskClean; exit' SIGTERM function TaskOne()
{
echo "Now do task one..."
sleep
echo "TaskOne is done"
} function TaskTwo()
{
echo "Now do task two..."
sleep
echo "TaskTwo is done"
} function TaskClean()
{
echo "Now do task clean..."
sleep
echo "TaskClean is done"
} TaskOne
TaskTwo
运行
horen@heart$~> sh signal.sh
Now do task one...
Now do task clean...
TaskClean is done
在执行 TaskOne 时给脚本发送信号 SIGTERM,脚本当时在执行 “sleep”,sleep 结束时转而去执行信号处理函数。注意,并不是在 TaskOne 结束后再去执行信号处理函数。
trap的一些使用说明:
它有三种形式分别对应三种不同的信号回应方式.
第一种:
trap 'commands' signal-list
当脚本收到 signal-list 清单内列出的信号时, trap 命令执行双引号中的命令.
第二种:
trap signal-list
trap 不指定任何命令, 接受信号的默认操作. 默认操作是结束进程的运行.
第三种:
trap ' ' signal-list
trap 命令指定一个空命令串, 允许忽视信号.
NOTE:trap 对同种 signal 只能相应一种设定,如果在一个 shell 里面设置多个 trap,如:
trap 'echo “aaaaaaaaaaa”' INT
trap 'echo “bbbbbbbbbbb”' INT
那么它只会响应最后一个信号设定。
信号处理(Signal Handling)在 Linux 编程中一直扮演者重要的角色,几乎每个系统工具都要用到它,最常见的功能莫过于用信号进行进程间通信(尤其是父子进程)以及捕捉 SIGINT、SIGTERM 之类的退出信号以做一些善后处理(cleanup)。C 中自不必多说,可以使用 wait 族函数;而 shell 脚本中也有捕捉信号的 trap 功能——然而许多人在使用 trap 功能的时候却存在着这样那样的误解,这些看似无关紧要的小细节最后有可能使得你的脚本与你预想的行为完全不同。
如无特殊说明,下文所指 shell 均以 Bash 为例。
0. trap 的使用简介
虽然我很想说这些应当要自己看 manpage ,但考虑到也许正在读文章的你手边没有 Linux ,还是简单说一下吧。
1
|
USAGE: trap [action condition ...]
|
即当捕捉到 condition 列表所对应的任何一个信号时,执行 action 动作(使用 eval action
来执行,故 action 可以是 shell 内建指令、外部命令及脚本中的函数等)。action 还可是”"(空)、’-'等,分别代表忽略相应信号及重置相应信号为默认行为。
1. condition 的标准格式是什么?
condition 中的信号到底应该如何书写?比如终端中断信号(一般用 CTRL-C 发出),到底是写 SIGINT 、 INT 还是 2(大部分系统上该信号对应的信号数)?是大写还是小写?
如果你使用最新版的 Bash ,那么这几种写法都可以。而如果你需要在不同 shell 中保持可移植性,请使用大写、不带前缀的 INT !根据 POSIX 标准, trap 的 condition 不应当加上 SIG 前缀,且必须全大写,允许带 SIG 前缀或小写是某些 shell 的扩展功能。而信号数在不同的系统上可能不同,所以也不是一个好主意。
2. trap 必须放在第一行么?
许多资料,尤其是中文资料中不容申辩地指明—— trap 必须放在脚本中第一个非注释行。事实果真如此么?
不论是 manpage 还是 POSIX 文档中,我都没有找到任何与之相关的说明。甚至在 TLDP 的 Bash Guide for Beginners 中,多个例子都分明把 trap 放在了脚本的中间。最后我在这篇文档中找到了下面这句经常被误读的话:
Normally, all traps are set before other executable code in the shell script is encountered, i.e., at the beginning of the shell script.
果然,这只是一个为了保证信号钩子尽早被设立的一个设计惯例罢了。事实上, trap 可以根据你的需要放在脚本中的任何位置。脚本中也可以有多个 trap ,可以为不同的信号定义不同的行为,或是修改、删除已定义的 trap 。更进一步地, trap 也有作用范围,你可以把它放在函数中,它将只在这个函数里起效!你看,其实 trap 的行为是很符合 UNIX 的惯例的。
3. 信号究竟是在什么时候被 trap 处理?
这是本文最重要的一点。信号到底是什么时候被处理的?更准确地说,比如脚本正在执行某个命令时收到了某个信号,那么它会被立即处理,还是要等待当前命令完成?
我不打算直接说明答案。为了让我们对这个问题有更透彻的理解,让我们来做一下实验。看下面这个时常被用来讲解 trap 的脚本:
1
2 3 |
#!/bin/bash
trap 'echo"INTERRUPTED!"; exit' INT sleep 100 |
大多数教程都是这么做的,运行这个脚本,按下 CTRL-C 。你看到了什么?脚本打出了 “INTERRUPTED!” 并停止了运行。这看起来似乎很正常、很直觉——以此看来, trap 会立即捕捉到信号并执行,不管当前正在执行的命令。许多脚本也正是在这个假设下写的。
然而真的是这样么?让我们做另一个实验——在一个终端执行这个脚本,并打开另一个终端,用ps-ef|grepbash
找到这个脚本的进程号,然后用kill-SIGINT pid
向这个进程发送 SIGINT 信号。你在原先的终端中看到了什么?没有任何反应!如果你愿意等上 100 秒,你最终会看到 “INTERRUPTED!” 被输出。这样看来 trap 是等到当前命令结束以后再处理信号。
这样的矛盾究竟是为什么?问题其实出在 CTRL-C 身上。 Bash 等终端的默认行为是这样的:当按下 CTRL-C 之后,它会向当前的整个进程组发出 SIGINT 信号。而 sleep 是由当前脚本调用的,是这个脚本的子进程,默认是在同一个进程组的,所以也会收到 SIGINT 并停止执行,返回主进程以后 trap 捕捉到了信号。
这篇文档给了我们一个更准确的说明——如果当前正有一个外部命令在前台执行,那么 trap 会等待当前命令结束以后再处理信号队列中的信号。(而许多教程出错的另一个原因就是——某些 shell 中 sleep 是内建命令,会被打断。)
那么上文的例子应当要如何写才能达到想要的效果呢?有两种方法:一、把 sleep 放到后台进行,再用内建的 wait 去等待其执行结束(详见上一段提到的那篇文档);二、暴力一点,把一长段 sleep 拆成一秒的小 sleep 的循环,这在对精度要求不高的情况下也是一个可行的办法。
shell中信号处理的更多相关文章
- linux shell 中的sleep命令
开始还以为是这样的语法: sleep(1), 后面发现是: linux shell 中的sleep命令 分类: LINUX 在有的shell(比如linux中的bash)中sleep还支持睡眠(分,小 ...
- shell 中的与、或表达式
今天总结一下linux shell中逻辑关机表达方式.逻辑与的表达: 1).if [ $xxx=a -a $xx=b ] 注:-a表示and的意思 2).if [ $xxx=a ] && ...
- shell简单用法笔记(shell中数值运算)二
shell中变量值,如果不手动指定类型,默认都是字符串类型: 例如: a= b= c=$a+#b echo $c 结果会输出:123+456 shell中,如果要进行数值运算,可以通过一下方法: 方法 ...
- shell中{}的妙用
shell中${}的妙用 1. 截断功能 ${file#*/}: 拿掉第一条/及其左边的字符串:dir1/dir2/dir3/my.file.txt ${file##*/}: 拿 ...
- shell中命令之间数据的传递
1.管道 "|" ls | cat -n > out.txt 2. 子shell 2.1 子shell 说明 在shell脚本中可以用()操作符可以定义一个子shell #/ ...
- shell中&&和||的使用方法
测试题: [ -z "" ] && echo 0 || echo 1 的结果是多少 看看这两个 && || 的用户 http://blog.csd ...
- 任督二脉之Shell中的正则表达式
VBird说学习Linux,掌握了Shell和正则就相当于打通了任督二脉,此后能力的成长才会突飞猛进. Shell的基础学习之前已经总结了一篇博客:http://www.cnblogs.com/jyz ...
- shell中条件判断if中的-z到-d的意思
shell中条件判断if中的-z到-d的意思 [ -a FILE ] 如果 FILE 存在则为真. [ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真. [ -c FILE ] 如果 ...
- shell中的循环
shell中的循环 for循环 类似于C语言的步长控制 例如: ;i<=;i++)); ); done 将1到10,依次乘以4,然后打印出来. 这里顺便提一下,shell里面表达式的计算,可以有 ...
随机推荐
- 51nod 1494 选举拉票 (线段树+扫描线)
1494 选举拉票 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注 现在你要竞选一个县的县长.你去对每一个选民进 ...
- 【刷题】BZOJ 3724 PA2014Final Krolestwo
Description 你有一个无向连通图,边的总数为偶数. 设图中有k个奇点(度数为奇数的点),你需要把它们配成k/2个点对(显然k被2整除).对于每个点对(u,v),你需要用一条长度为偶数(假设每 ...
- linux运维之分析系统负载及运行状况
1.删除0字节文件 find -type f -size -exec rm -rf {} \; 2.查看进程 #按内存从大到小排列 ps -e -o "%C : %p : %z : %a&q ...
- 【bzoj1492】 NOI2007—货币兑换Cash
http://www.lydsy.com/JudgeOnline/problem.php?id=1492 (题目链接) 题意 两种金券,金券按照比例交易:买入时,将投入的资金购买比例为$rate[i] ...
- POJ 1502 MPI Maelstrom / UVA 432 MPI Maelstrom / SCU 1068 MPI Maelstrom / UVALive 5398 MPI Maelstrom /ZOJ 1291 MPI Maelstrom (最短路径)
POJ 1502 MPI Maelstrom / UVA 432 MPI Maelstrom / SCU 1068 MPI Maelstrom / UVALive 5398 MPI Maelstrom ...
- 【洛谷P1126】机器人搬重物
题目大意:给定一个 N 行,M 列的地图,一个直径为 1.6 的圆形机器人需要从起点走到终点,每秒钟可以实现:向左转,向右转,向前 1-3 步.求从起点到终点最少要多长时间. 题解:相比于普通的走迷宫 ...
- git 学习小记之图形化界面客户端
习惯了 Windows 的用户,一直不喜欢用类似命令行的东西来操作,当然我也不是不喜欢,只是操作太慢了.也许 Linux 大神在命令行的帮助下,办事效率翻倍,那也是非常常见的事情..当然我不是大神,所 ...
- 利用 ASP.NET 的内置功能抵御 Web 攻击 (1)
摘要: Dino 总结了最常见的 Web 攻击类型,并介绍了 Web 开发人员可以如何使用 ASP.NET 的内置功能来改进安全性. 一.ASP.NET 开发人员应当始终坚持的做法 如果您正在阅读本文 ...
- 【BZOJ】1095: [ZJOI2007]Hide 捉迷藏 括号序列+线段树
[题目]BZOJ 1095 [题意]给定n个黑白点的树,初始全为黑点,Q次操作翻转一个点的颜色,或询问最远的两个黑点的距离,\(n \leq 10^5,Q \leq 5*10^5\). [算法]括号序 ...
- 20155233 2016-2017-2 《Java程序设计》第5周学习总结
20155233 2016-2017-2 <Java程序设计>第5周学习总结 学习目标 理解异常架构 牚握try...catch...finally处理异常的方法 会用throw,thro ...