免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3149 | 回复: 0
打印 上一主题 下一主题

[总结]void signal(int signo,..)的一些问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-07-15 13:16 |只看该作者 |倒序浏览
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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP