Chinaunix首页 | 论坛 | 博客
  • 博客访问: 81162
  • 博文数量: 28
  • 博客积分: 579
  • 博客等级: 中士
  • 技术积分: 245
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-30 18:17
文章分类

全部博文(28)

文章存档

2012年(1)

2011年(27)

我的朋友

分类: Python/Ruby

2011-12-05 15:07:08

转载文章请注明,转载自:扶凯[]

本文链接:

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. #!/usr/bin/perl
  2. $SIG{TERM} = $SIG{INT} =\&yoursub;
  3. my $i = 1;
  4. while(1){
  5.         sleep 1;
  6.         $i = $i +1;
  7.         print $i."\n";
  8. }
  9. sub yoursub{
  10.         print " exit ... \n";
  11.         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. #!/usr/bin/perl
  2. use strict;
  3. BEGIN {
  4.      # fatal handler setting.
  5.      $SIG{__DIE__} = $SIG{__WARN__} = \&handler_fatal;
  6. }
  7.   
  8. sub handler_fatal {
  9. print "Content-type: text/html\n";
  10. print "@_&";
  11. }

当然perl的CGI的500错误,用下面的模块CGI::Carp来处理会更加容易些

 
 
use CGI::Carp qw(fatalsToBrowser);
die  'Bad error here';

另一个常用的用法使用$SIG{ALRM},设置等待超时一般都这样做:

  1. local $SIG{ALRM} = sub { alarm 0;die "TIMEOUT"; }; #超时处理过程
  2. eval {
  3.     alarm(10); #设定10秒钟后如果下面的代码没处理完,则进入超时处理过程
  4.     $input = <>; #处理过程
  5.     alarm(0); #如果处理完了,取消超时处理设置
  6. };
  7. if ($@ =~ /TIMEOUT/) { ... }
注意这里alarm(10)一定要放在eval内。否则,万一程序执行完alarm后发生任务切换,而程序再次获得时间片时,ALRM信号已经发生, 这时程序还没有执行到eval内就产生die,程序就会退出
 

有时我们要杀死所有的子进行,需要用到向进程组发送信息

在perl中,进程组的ID就是$$.如果程序想给所有由它启动的所有子进程发送一个挂起的信号号,现实的方法,先调用setpgrp(0,0),使自己成为新的进程组的领头.这样不管是fork还是open,还是system都无所谓.

当然先排除自己

  1. { local $SIG{HUP} = 'IGNORE'; # 排除自己\ kill(HUP,-$$); #通知自己的进程组 }
Fork所做的事情

父进程将代码段,堆栈段,数据段完全复制一份给子进程。也就是说,在子进程运行之初,它拥有父进程的一切变量和句柄。例如,父进程申明了某个hash表,那这个hash表也会被子进程拥有。
然而,一旦子进程开始运行,它的数据段和堆栈段就在内存里完全和父进程分离开了。也就是说,两个进程间不再共享任何数据。例如前面所说的hash表,虽然子进程从父进程处继承了这个数据结构,但子进程写往hash里的数据,不会被父进程访问到。

Fork出来的僵死进程

如果进程退出时,会向父发送一个CHLD的信号后,就会变成僵死的进程,需要父进程使用wait和waitpid来收割.当然,也可以设置$SIG{CHLD}为IGNORG

在 Unix 系统上守护进程化

对于 Fork  出来的程序,我们常常需要给进程推到后台,不然在前台程序会一直占用,当关掉当前 shell 时,程序就退出了。如下有个 <>中一个非常合适的例子:

  1. #!/usr/bin/perl

  2. use strict;

  3. use POSIX qw(setsid);



  4. nohup(@ARGV);



  5. sub nohup {

  6.     my $child = fork();

  7.     die $! unless defined $child;

  8.     exit 0 if $child;

  9.     setsid();

  10.     

  11.     open (STDIN, "<", "/dev/null");

  12.     open (STDOUT, ">", "nohup.out");

  13.     open (STDERR, ">&", STDOUT);



  14.     exec "@_";

  15. }
  1. sub daemonStart {
  2.     die "Can't fork:$!" unless defined (my $child = fork);
  3.     exit 0 if $child;
  4.   
  5.     open STDIN, "/dev/null" or die "Can't write to /dev/null: $!";
  6.     #open STDERR, ">&STDOUT" || die ("open STDERR failed");
  7.   
  8.     # 分离终端和进程组
  9.     setsid() or die "Can't start a new session.";
  10.   
  11.     chdir '/'; #修改工作目录
  12.     umask(0); #设置文件的 umask
  13.     $ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin";
  14.     return $$;
建议使用 Proc::Daemon 模块
阅读(1180) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~