Perl中对信号和并发的处理
转载本站文章请注明,转载自:扶凯[http://www.php-oa.com]
本文链接: http://www.php-oa.com/2009/06/09/perl_signal.html
linux中的信号
先了解在linux中的信号,信号其实就是编程里俗称的中断,它使监视与控制其他进程变为有可能。中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程发生了什么事件,并不给该进程传递任何数据。
首先看看linux中的常用信号,见如下列表:
========================================================================
信号名 值 标注 解释
————————————————————————
HUP 1 A 检测到挂起
INT 2 A 来自键盘的中断
QUIT 3 A 来自键盘的停止
ILL 4 A 非法指令
ABRT 6 C 失败
FPE 8 C 浮点异常
KILL 9 AF 终端信号
USR1 10 A 用户定义的信号1
SEGV 11 C 非法内存访问
USR2 12 A 用户定义的信号2
PIPE 13 A 写往没有读取者的管道
ALRM 14 A 来自闹钟的定时器信号
TERM 15 A 终端信号
CHLD 17 B 子进程终止
CONT 18 E 如果被停止则继续
STOP 19 DF 停止进程
TSTP 20 D tty键入的停止命令
TTIN 21 D 对后台进程的tty输入
TTOU 22 D 对后台进程的tty输出
————————————————————————
著明:上表中‘值’列下没有列出的值所对应的信号为系统调用的非标准信号。上表中的第三列‘标注’定义了当进程接受到信号后的默认的操作
A—–终止进程
B—–忽略进程信号
C—–终止进程并卸下内核
D—–停止进程
E—–恢复进程
F—–不能截取或忽略进程信号
========================================================================
Perl中命令信号的原理
Perl 提供了%SIG 这个特殊的默认HASH.调用需要使用到系统保留全局HASH数组%SIG,即使用’$SIG{信号名}’截取信号,相当于,在perl程序中出现这个信号时,执行我们自己定义某段代码(子函数)的地址值(定义信号响应函数),这代码就是截取这个信息后要执行的结果了。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 | #!/usr/bin/perl $SIG{TERM} = $SIG{INT} =/&yoursub; my $i = 1; while(1){ sleep 1; $i = $i +1; print $i."/n"; } sub yoursub{ print " exit ... /n"; exit } |
最程序运行前,我们给$SIG{TERM}和$SIG{INT}二个hash放一个子函数的引用(地址),当有终端信号或来自键盘的中断时,上面的while中的就不在运行,就开始运行yoursub这个函数.
可以使用的地方
象对信息的处理,我们常用到的地方,可以捕捉die及一些warning的信息,然后打印出来,我们也可以让程序在退出来之前,就是按下Ctrl+c时,进行一些任务(如删除tmp文件),需要注意的地方是.为了尽可能早的加载这些代码,这样就能保证程序一执行就能先得到信号。 这样用处非常大,比如我们写的perl的CGI.可以用信号来捕捉CGI程序Internal 500 错误,不然出现了问题,大多数都必须查看Web server的日志才能知道程序哪里出了错误,页面只一个500服务器错误,象php因为是mod,直接就显示在网页上。 可以使用如下的方法.比如将信号捕捉代码放到BEGIN块中
1 2 3 4 5 6 7 8 9 10 11 | #!/usr/bin/perl use strict; BEGIN { # fatal handler setting. $SIG{__DIE__} = $SIG{__WARN__} = /&handler_fatal; } sub handler_fatal { print "Content-type: text/html/n"; print "@_&"; } |
当然perl的CGI的500错误,用下面的模块CGI::Carp来处理会更加容易些
1 2 | use CGI::Carp qw(fatalsToBrowser); die 'Bad error here'; |
另一个常用的用法使用$SIG{ALRM},设置等待超时一般都这样做:
1 2 3 4 5 6 7 | local $SIG{ALRM} = sub { alarm 0;die "TIMEOUT"; };#超时处理过程 eval { alarm(10); #设定10秒钟后如果下面的代码没处理完,则进入超时处理过程 $input = <>; #处理过程 alarm(0); #如果处理完了,取消超时处理设置 }; if ($@ =~ /TIMEOUT/) { ... } |
注意这里alarm(10)一定要放在eval内。否则,万一程序执行完alarm后发生任务切换,而程序再次获得时间片时,ALRM信号已经发生, 这时程序还没有执行到eval内就产生die,程序就会退出
有时我们要杀死所有的子进行,需要用到向进程组发送信息
在perl中,进程组的ID就是$$.如果程序想给所有由它启动的所有子进程发送一个挂起的信号号,现实的方法,先调用setpgrp(0,0),使自己成为新的进程组的领头.这样不管是fork还是open,还是system都无所谓.
当然先排除自己
1 2 3 4 | { local $SIG{HUP} = 'IGNORE'; # 排除自己/ kill(HUP,-$$); #通知自己的进程组<p> </p> } |
Fork所做的事情
父进程将代码段,堆栈段,数据段完全复制一份给子进程。也就是说,在子进程运行之初,它拥有父进程的一切变量和句柄。例如,父进程申明了某个hash表,那这个hash表也会被子进程拥有。
然而,一旦子进程开始运行,它的数据段和堆栈段就在内存里完全和父进程分离开了。也就是说,两个进程间不再共享任何数据。例如前面所说的hash表,虽然子进程从父进程处继承了这个数据结构,但子进程写往hash里的数据,不会被父进程访问到。
Fork出来的僵死进程
如果进程退出时,会向父发送一个CHLD的信号后,就会变成僵死的进程,需要父进程使用wait和waitpid来收割.当然,也可以设置$SIG{CHLD}为IGNORG
不错的Fork的模块
1 2 3 4 5 6 7 8 | use Parallel::ForkManager; $pm = new Parallel::ForkManager($MAX_PROCESSES)#标明最大进程数。0 是不 fork . foreach $data (@all_data) { my $pid = $pm->start and next; # $pm->start 来开始 fork.$pm 是子进程时返回 0 ,父进程时返回子进程的进程.“and next” 用来跳过父进程。 ...子进程中... $pm->finish; #结束子程序 } |