- 论坛徽章:
- 0
|
revision history:
July 15 09 create
signal函数是UNIX最简单的处理信号的函数:
void (*signal(int signo,void (*func)(int)))(int);
参数:
signo:你要处理的信号名,SIGxxx(such as SIGINT)
func:你要对这个信号定义什么样的处理方式
SIG_IGN -- 当发生这个信号时,忽略它
SIG_DFL -- 当发生这个信号时,采用系统默认的方式
your_defined_func -- 当发生这个信号时,调用函数func()
早期的信号处理是不可靠的(可能会丢失),如在V7中。
1. SVR4也提供signal函数,该函数可提供老的SVR2不可靠信号语义.提供此函数主要是为了向下兼容要求此老语义的应用程序. 我在公司的UNIX服务器(SunOS 5.9)上测试了下面的sample code,可以出现下面的issue1,2,3.证明SunOS 5.9提供的signal函数是不可靠的,是不是为了兼容以前的版本??
所以在不能对signal函数提供可靠的机制的机器上的新应用程序不应使用它。(need more test).
2. 4.3+BSD提供的signal函数是用sigaction函数实现的。所以在4.3+BSD之下使用它提供新的可靠的信号语义.
3. 我在家里的机器(Linux Redhat 9.0)上测试了下面的sample code.可以看出是可靠的。
$ uname -a
Linux localhost.localdomain 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux
$ ./sigusr.o &
[1] 2866
$ kill -USR1 2866
received SIGUSR1
$ kill -USR1 2866
received SIGUSR1
$ kill -USR2 2866
received SIGUSR2
$ kill -USR2 2866
received SIGUSR2
$ kill -USR2 2866
received SIGUSR2
$ kill 2866
[1]+ Terminated ./sigusr.o
我们可以看出,进程接受到一次SIGUSR1信号后,我们继续发送SIGUSR1,进程还是能按照我们期待的方法进行处理,并没有将对该信号的处理更改为默认值SIG_DFL. 所以不会出现下面的issue 1。
是不是linux中的signal(...)函数是用的sigaction(...)实现的???
linux从unix中继承的是可靠的signal函数???
-------------Sample code(10-1):----------------
test OS:SunOS 5.9
SunOS zch45bd5 5.9 Generic_122300-33 sun4u sparc SUNW,Sun-Fire-V440
#include
#include "ourhdr.h"
static void sig_usr(int); /* one handler for both signals */
int main(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR2");
for ( ; ; )
pause();
}
static void sig_usr(int signo) /* argument is signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n", signo);
return;
}
--------Issues--------
issue 1:进程每次处理信号时,随即将信号动作复置为默认值
当发生了某个信号,并且这个信号设置成了由用户捕捉,在进入用户信号处理函数后,就将以后对该信号的处理重新设为默认的值(SIG_IGN)。这样就造成了这种信号是不可靠的信号。因为这种信号可能会
a.以后处理对该信号的处理被修改成了默认方法,这可能不是用户想要的。
b.造成意外的结果,如终止进程。
比如在捕捉到第一次信号(SIGUSR1)时,进入信号处理函数(sig_usr),这时系统已经将对该信号处理修改成了默认处理方式SIG_DFL。而如果这时该信号又出现了一次(将会按照默认方式处理,可能是终止进程),而第一次的处理还没有结束。这时进程就会被第二次出现的该信号意外终止。
即使用了下面的方法来弥补这个缺陷,但是还是有个时间窗口,在这个窗口中,还是会出现第二次出现的信号终止了进程的现象。
int sig_int();
...
signal(SIGINT,sig_int);
sig_int()
{//这里就是可能出现问题的时间窗口,运行到这里的时候如果再次出现了这个信号,那么系统就会用默认的方式来处理这个信号(很可能是终止进程)
signal(SIGINT,sig_int);
...
}
details:在信号发生之后到信号处理程序中调用signal函数之间有一个时间窗口。在此段时间中,可能发生另一次中断信号。第二个信号会造成执行默认动作,而对中断信号则是终止该进程.
注:
1)如果是将信号的处理方式设置成SIG_IGN,在处理一次该信号后并没有将对该信号的处理方式修改成SIG_DFL. issue 1讲的是用户自己定义信号处理函数的情况。
main(void)
{
if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
err_sys("can't catch SIGUSR1");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR2");
if (signal(SIGTERM, sig_term) == SIG_ERR)
err_sys("can't catch SIGTERM");
for ( ; ; )
pause();
}
static void sig_usr(int signo) /* argument is signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n", signo);
return;
}
$./a.out &
[1] 18289
$kill -USR1 18289
$kill -USR1 18289
$kill -USR1 18289
$kill -USR2 18289
received SIGUSR2
我们可以看出进程在接受到SIGUSR1后,采用的都是SIG_IGN的方式,并没有在处理过一次SIGUSR1后将该信号处理方式更改成SIG_DFL(终止该进程)。
issue 2:不调用signal函数就无法知道我们当前对此信号的处理动作是什么。
issue 3:无法实现对该信号的阻塞。
如果我们希望内核阻塞一个信号(不是忽略),然后在以后的可以解除对该信号的阻塞,让进程知道,这个信号曾经发生过,并进行相应的处理。
下面的例子可以看到不能阻塞一个信号带来的问题---信号丢失
code。
int sig_flag=0;
int main(void)
{
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("can't catch SIGUSR2");
if (signal(SIGTERM, sig_term) == SIG_ERR)
err_sys("can't catch SIGTERM");
while(sig_flag ==0)
{//这里是一个可能丢失信号的时间窗口,如果这个时候发生了信号,并处理返回了,下面的pause()将无法退出(如果这个信号不再发生的话)
printf("testing sig_flag:%d\n",sig_flag);
pause();
printf("catch a signal ,and have handled it \n");
}
printf("end main\n");
}
static void sig_term(int signo)
{
printf("received SIGTERM\n");
signal(SIGTERM,sig_term);
sig_flag=1;
printf("set sig_flag to %d\n",sig_flag);
return;
}
static void
sig_usr(int signo) /* argument is signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
err_dump("received signal %d\n", signo);
return;
}
$./a.out &
[1] 21621
$testing sig_flag:0
$kill -USR2 21621 //发送SIGUSR2信号给进程
received SIGUSR2 //1.先处理信号SIGUSR2,
catch a signal ,and handled it //2.然后再退出pause();函数
testing sig_flag:0 //SIGUSR2并没有将sig_flag 设置成非0,所以继续循环
$kill 21621 //发送SIGTERM信号给进程
received SIGTERM //1.进程接捕捉到SIGTERM信号,
set sig_flag to 1 //并再sig_term()中进行处理,将sig_flag 设置成1
catch a signal ,and handled it //2. 信号处理完,退出pause()函数
end main//这时loop的条件不满足了,sig_flag为1。退出main
comments:
1. pause()函数等待信号,信号处理完后退出pause(),注意这个顺序,不是退出pause()后,处理信号。
2. 这个程序在大多数情况下都是能正常工作的,main函数等待信号,接受到一个信号,并处理后,检测sig_flag是否为非0,如果为非0则退出loop,结束进程。但是这里也有一个时间窗口。就是在
while(sig_flag ==0)
{//point 1这里是一个可能丢失信号的时间窗口,如果这个时候发生了信号,并处理返回了,下面的pause()将无法退出(如果这个信号不再发生的话)
printf("testing sig_flag:%d\n",sig_flag);
pause();
printf("catch a signal ,and have handled it \n");
}
如果我们修改一下程序来模拟由于进程调度的原因,造成在point 1出产生信号的情况。
while(sig_flag ==0)
{//point 1
printf("sleep 10 sec\n");
sleep(10);//here we got a TERM signal
printf("testing sig_flag:%d\n",sig_flag);
pause();
printf("catch a signal ,and have handled it \n");
}
$./a.out &
[1] 27731
sleep 15 sec
$kill 27731 //发送SIGTERM信号
received SIGTERM //处理信号
set sig_flag to 1testing sig_flag:1 //将sig_flag设置成1
$//我们在这里等待了1 min,还没有看到进程结束,因为这个信号丢失了。
$ps
PID TTY TIME CMD
27784 pts/150 0:00 ps
27731 pts/150 0:00 a.out
17880 pts/150 0:00 tcsh
17879 pts/150 0:00 cleartoo
17835 pts/150 0:00 tcsh
可以看出,我们模拟了在sleep 15 sec的时候产生信号,这时候的信号虽然将sig_flag设置成了非0,但是在pause中等待信号时,如果这个信号再也没有出现过,则就出现了问题,造成了由于信号丢失而永远无法退出loop。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/59712/showart_1996754.html |
|