在C,我可以说
#include <stdio.h> #include <unistd.h> #include <signal.h> int continue_running = 1; void handler(int signal,siginfo_t* info,void* data) { printf("got signal %d from process %d running as user %d\n",signal,info->si_pid,info->si_uid); continue_running = 0; } int main(int argc,char** argv) { struct sigaction sa; sigset_t mask; sigemptyset(&mask); sa.sa_sigaction = &handler; sa.sa_mask = mask; sa.sa_flags = SA_SIGINFO; sigaction(SIGTERM,&sa,NULL); printf("pid is %d\n",getpid()); while (continue_running) { sleep(1); }; return 0; }
这打印出来的东西
pid is 31980 got signal 15 from process 31985 running as user 1000
当从过程31985发送SIGTERM时。
我可以写类似的Perl 5代码使用POSIX :: sigaction:
#!/usr/bin/perl use strict; use warnings; use POSIX; use Data::Dumper; my $sigset = POSIX::SigSet->new; $sigset->emptyset; my $sa = POSIX::SigAction->new( sub { print "caught signal\n" . Dumper \@_; $a = 0 },$sigset,); $sa->flags(POSIX::SA_SIGINFO); $sa->safe(1); #defer the signal until we are in a safe place in the intrepeter POSIX::sigaction(POSIX::SIGTERM,$sa); print "$$\n"; $a = 1; sleep 1 while $a;
但是处理程序仍然只接收一个参数(信号)。如何获得siginfo_t结构?是否必须编写自己的XS代码设置自己的处理程序,然后将信息传递到Perl回调?在XS中编写自己的处理程序在某种程度上解开了解释器?
解决方法
sighandler(在mg.c中)是Perl信号处理程序子程序的包装器。正如你可以看到,它是能够发送你想要的信息到Perl信号处理程序子。
#if defined(HAS_SIGACTION) && defined(SA_SIGINFO) { struct sigaction oact; if (sigaction(sig,&oact) == 0 && oact.sa_flags & SA_SIGINFO) { if (sip) { HV *sih = newHV(); SV *rv = newRV_noinc(MUTABLE_SV(sih)); /* The siginfo fields signo,code,errno,pid,uid,* addr,status,and band are defined by POSIX/SUSv3. */ (void)hv_stores(sih,"signo",newSViv(sip->si_signo)); (void)hv_stores(sih,"code",newSViv(sip->si_code)); #if 0 /* XXX TODO: Configure scan for the existence of these,but even that does not help if the SA_SIGINFO is not implemented according to the spec. */ hv_stores(sih,"errno",newSViv(sip->si_errno)); hv_stores(sih,"status",newSViv(sip->si_status)); hv_stores(sih,"uid",newSViv(sip->si_uid)); hv_stores(sih,"pid",newSViv(sip->si_pid)); hv_stores(sih,"addr",newSVuv(PTR2UV(sip->si_addr))); hv_stores(sih,"band",newSViv(sip->si_band)); #endif EXTEND(SP,2); PUSHs(rv); mPUSHp((char *)sip,sizeof(*sip)); } } } }
你想要的信息将在最后一个参数,虽然你必须解压* sip自己的Perl端。抓住的是,上面的代码没有得到excercised。具体来说,sip总是NULL。
在不安全信号下,sighandler从csighandler,Perl的C级信号处理程序调用。它目前不传递相关信息给signalhandler,但这很容易修复。
-Perl_csighandler(int sig,siginfo_t *sip PERL_UNUSED_DECL,void *uap PERL_UNUSED_DECL) +Perl_csighandler(int sig,siginfo_t *sip,void *uap PERL_UNUSED_DECL) - (*PL_sighandlerp)(sig,NULL,NULL); + (*PL_sighandlerp)(sig,sip,NULL);
样品运行:
$ PERL_SIGNALS=unsafe ./perl -Ilib a.pl 31213 caught signal $VAR1 = [ 'TERM',{ 'code' => 0,'signo' => 15 },'...*sip as "packed/binary" string...' ];
在安全信号下,通过PERL_ASYNC_CHECK从despatch_signals(sic)调用sigandler。不幸的是,先前由csighandler接收的* sip不再可用。要解决这个问题,csighandler必须将* sip的副本排队等待despatch_signals获取。