Chinaunix首页 | 论坛 | 博客
  • 博客访问: 180341
  • 博文数量: 44
  • 博客积分: 832
  • 博客等级: 准尉
  • 技术积分: 368
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 16:28
文章分类

全部博文(44)

文章存档

2013年(17)

2012年(27)

我的朋友

分类: Python/Ruby

2012-12-21 22:42:12

http://blog.sina.com.cn/s/blog_62eb16bb01014dbh.html

一. trap捕捉到信号之后,可以有三种反应方式:

  (1)执行一段程序来处理这一信号

  (2)接受信号的默认操作

  (3)忽视这一信号

  二. trap对上面三种方式提供了三种基本形式:

  第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,将执行双

  引号中的命令串。

  trap 'commands' signal-list

  trap "commands" signal-list

  为了恢复信号的默认操作,使用第二种形式的trap命令:

  trap signal-list

  第三种形式的trap命令允许忽视信号

  trap " " signal-list

  注意:

  (1) 对信号11(段违例)不能捕捉,因为shell本身需要捕捉该信号去进行内存的转储。

  (2) 在trap中可以定义对信号0的处理(实际上没有这个信号), shell程序在其终止(如

  执行exit语句)时发出该信号。

  (3) 在捕捉到signal-list中指定的信号并执行完相应的命令之后, 如果这些命令没有

  将shell程序终止的话,shell程序将继续执行收到信号时所执行的命令后面的命令,这样将

  很容易导致shell程序无法终止。

  另外,在trap语句中,单引号和双引号是不同的,当shell程序第一次碰到trap语句时,

  将把commands中的命令扫描一遍。此时若commands是用单引号括起来的话,那么shell不会

  对commands中的变量和命令进行替换, 否则commands中的变量和命令将用当时具体的值来

  替换。


linux shell编程 trap命令

trap命令用于指定在接收到信号后将要采取 的行 动,我们将在本书后面的内容中详细介绍信号。trap命令的一种常见用途是在脚本程序被中断时完成清理工作。历史上,shell总是用数字来代表信号,而 新的脚本程序应该使用信号的名字,它们保存在用#include命令包含进来的signal.h头文件中,在使用信号名时需要省略SIG前缀。你可以在命 令提示符下输入命令trap -l来查看信号编号及其关联的名称。

对于那些不熟悉信号的人们来说,“信号”是指那些被异步发送到一个程序的事件。默认情况下,它们通常会终止一个程序的运行。

trap命令的参数分为两部分,前一部分是接收到指定信号时将要采取的行动,后一部分是要处理的信号名

 

请记住,脚本程序通常是以从上到下的顺序解释执行的,所以必须在你想保护的那部分代码以前指定trap命令。

如果要重置某个信号的处理条件到其默认值,只需简单的将command设置为-。如果要忽略某个信号,就把command设置为空字符串‘’。一个不带参数的trap命令将列出当前设置的信号及其行动的清单。

表2-11列出了X/Open规范里面规定的能够被捕获的比较重要的一些信号(括号里面的数字是传统的信号编号)。更多细节请参考signal在线手册的第七部分(man 7 signal)。

表 2-11

信     号

说     明

HUP(1)

挂起,通常因终端掉线或用户退出而引发

INT(2)

中断,通常因按下Ctrl+C组合键而引发

QUIT(3)

退出,通常因按下Ctrl+组合键而引发

ABRT(6)

中止,通常因某些严重的执行错误而引发

ALRM(14)

报警,通常用来处理超时

TERM(15)

终止,通常在系统关机时发送

实验:信号处理

下面的脚本演示了一些简单的信号处理方法:

 

 

 

运行这个脚本,在每次循环时按下Ctrl+C组合键(或任何你系统上设定的中断键),我们将得到如下所示的输出:

 

实验解析

在这个脚本程序中,我们先用trap命令安排它在出 现一个INT(中断)信号时执行rm –f /tmp/my_tmp_file_$$命令删除临时文件。脚本程序然后进入一个while循环,只要临时文件存在,循环就一直持续下去。当用户按下 Ctrl+C组合键时,就会执行rm –f /tmp/my_tmp_file_$$语句,然后继续下一个循环。因为临时文件现在已经被删除了,所以第一个while循环将正常退出。

接下来,脚本程序再次调用trap命令,这次是指定 当一个INT信号出现时不执行任何命令。脚本程序然后重新创建临时文件并进入第二个while循环。这次当用户按下Ctrl+C组合键时,没有语句被指定 执行,所以采取默认处理方式,即立即终止脚本程序。因为脚本程序被立即终止了,所以永远也不会执行最后的echo和exit语句。

16.unset命令

unset命令的作用是从环境中删除变量或函数。这个命令不能删除shell本身定义的只读变量(如IFS)。这个命令并不常用。

下面的脚本第一次输出字符串“Hello World”,但第二次只输出一个换行符:

 

使用foo=语句产生的效果与上面脚本中的unset命令产生的效果差不多,但并不等同。foo=语句将变量foo设置为空,但变量foo仍然存在。而使用unset foo语句的效果是把变量foo从环境中删除




ctrl+c,涉及到前台进程组,和终端设置属性有关。
其意义就是说,当按下ctrl+c时候,(前提是stty -a看到,intr=^C,很多系统默认是del),终端驱动程序检测到,并且根据这个stty的设定,给每个属于前台进程组(进程组可以有很多,但是任一时刻,只能有一个前台进程组),给该组的每个进程都发送SIGINT。
首先,要明确,一般的shell实现而言,如果是用交互式shell来运行,
为了方便问题叙述,假定我们现在的shell是bash
那么ksh -i -c "cmd"和ksh -c "cmd"
在ksh -i 模式下,cmd和ksh不是一个进程组,ksh和bash又不是一个进程组。
在ksh直接-c没有-i模式下,一般的实现而言,ksh和cmd是一个进程组,而ksh和bash不是一个进程组。
为什么是这个结果? 想想看?

这样我们可以分析,
有个x.sh文件
trap "cmd" sig
xxx
xxx

我们会sh  x.sh 或者直接x.sh
不过不论什么模式,都会有当前的登录shell启动一个子shell来执行这个x.sh。而且,该shell和x.sh中需要启动的子进程都是一个进程组(观察到这点很重要)。因为如果不是交互模式的shell来执行,则该子shell和其子进程都会属于一个进程组(除非被调用的cmd又重新设置了其组id),否则,子shell和其子shell属于不同的进程组。为什么ctrl+c会中断该子shell和其子进程呢(不论是否交互shell启动),原因就在于,当非交互式启动,他们自然是一个组,都属于同一个前台进程组。当交互式启动时候,他们不属于同一个进程组,但是请注意,一个进程只要有控制终端(ps -f时候,能看到tty不是?的就是),就能让自己参加该前台进程组(注意,参加前台进程组,不会改变自己的组id)。事实上,我们也可以看到,在交互模式启动下,子shell和其子进程不是一个组,但是他们也属于同一个前台进程组。并且该前台进程组号就是子shell的子进程的组号。因此我们可以推想,子shell,为了一直保持和其子进程在同一个前台进程组,不断的调用了tcsetpgrp,来参加其子进程所在的组。否则,按照现在unix的规则,就会发现,ctrl+c后,子shell不会终止,但是其子进程会终止!这当然不是我们想要的。

以上的阐述,综合起来就是三句话:
1,ctrl+c(如果就是终端产生SIGINT设置的话),会送到当前的前台进程组的每一个进程。
2,前台进程组里的进程,可以都是同一个组,也可以不是同一个组。
3,前台进程组是一个终端属性,同一终端,任一时刻,最多只能有一个前台进程组。

事实上,shell trap一个信号,并非是wait子进程的退出态,如果不是正常结束(128+信号数值),就调用trap。恰恰相反,是shell为自己设定了一个信号处理,因为他和子进程是同一个前台进程组,因此,可以得到从终端驱动发送到该组每个成员的一个信号(并非shell无法直接获得SIGINT),在自己收到SIGINT时候,即触发自己设定的动作。

这一点,其实可以通过一个简单的实验证实:
x.sh在trap 后,执行调用一个很小的C程序编译后的binary,该C文件实现为一直等待,捕捉SIGINT后exit(0)。这样x.sh就会认为该子进程是正常结束?按照cjaizss 的说法,就会得出,x.sh本身不会触发trap的动作。实际上,肯定会的。其实有个更简单的测试。

sh -c "trap 'echo ok' 2; sh -c \"trap 'exit 0' 2; read\""

阅读(3374) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~