免费注册 查看新帖 |

Chinaunix

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

[Linux] 为什么信号SIGUSR1只能捕捉一次 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-06-03 09:45 |只看该作者 |倒序浏览
练习APUE上面一个利用信号同步的一个例子,发现SIGURS1只能捕捉一次,我把问题简化后,代码是这样的:
     1  #include "apue.h"
     2  static volatile sig_atomic_t sigflag=0;   /* set nonzero by sig handler */
     3  static sigset_t newmask, oldmask, zeromask;
     4  static void sig_usr(int signo)
     5  {
     6          sigflag = 1;
     7  }
     8
     9  void TELL_WAIT(void)
    10  {
    11          if (signal(SIGUSR1, sig_usr) == SIG_ERR)
    12                  err_sys("signal(SIGUSR1) error");
    13          sigemptyset(&zeromask);
    14          sigemptyset(&newmask);
    15          sigaddset(&newmask, SIGUSR1);
    16          if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
    17                  err_sys("SIG_BLOCK error");
    18  }
    19
    20  void WAIT(void)
    21  {
    22          while (sigflag == 0)
    23                  sigsuspend(&zeromask);
    24          sigflag = 0;
    25          if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    26                  err_sys("SIG_SETMASK error");
    27  }
    28
    29  int main()
    30  {
    31          int value = 0, fd;
    32          pid_t pid;
    33          FILE *fp;
    34          fp=stdout;
    35          setbuf(fp, NULL);
    36          TELL_WAIT();
    37          for (int i = 0; i < 5; i++) {
    38                  WAIT();
    39                  fprintf(fp, "parent : %d\n", value++);
    40          }
    41          exit(0);
    42  }

然后编译成 a.out。
我想每次发送一个信号后,进程输出一个自增的数,下面是过程:
$ ./a.out &
[1] 3164
$ kill -USR1 3164
parent : 0
$ kill -USR1 3164
[1]+  User defined signal 1   ./a.out

我的问题是:为什么 SIGURS1 只能捕捉一次?我练习其他的程序时 SIGURS1 都是可以重复捕捉的,handler 不用重新注册
望赐教,非常感谢! tmp.c.word (1.01 KB, 下载次数: 2)



论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
2 [报告]
发表于 2014-06-06 16:33 |只看该作者
         while (sigflag == 0)
    23                  sigsuspend(&zeromask);

这代码显然有竞争,如果信号在while之后sigsuspend之前发生你岂不是就永远挂死了?

如果你能理解mutex+cond的用法,那sigprocmask+sigsuspend的用法你应该也能自己想出来。

论坛徽章:
0
3 [报告]
发表于 2014-06-06 19:08 |只看该作者
你确定理解了 sigsuspend ?
sigsuspend 在调用之前和返回之后,SIGUSR1 都是被阻塞的,只有在sigsuspend执行期间(即挂起), SIGUSR1 才可能送达并被处理
回复 2# linux_c_py_php


   

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
4 [报告]
发表于 2014-06-06 22:24 |只看该作者
本帖最后由 timespace 于 2014-06-06 22:26 编辑

copy上面的代码,在APUE 2e环境编译,重现不了你的问题:
$ ./test &
[1] 2224
$ kill -sigusr1 2224
parent : 0
$ kill -sigusr1 2224
parent : 1
$ kill -sigusr1 2224
parent : 2
$ kill -sigusr1 2224
parent : 3
$ kill -sigusr1 2224
parent : 4
$ kill -sigusr1 2224
-bash: kill: (2224) - No such process
[1]+  Done                    ./test
$ uname -r
2.6.32-431.11.2.el6.x86_64


上面的代码确实有race condition,手动不好重现,不过起码现在用法有问题
第一次WAIT()前:SIGUSR1是被BLOCK的,只是sigsuspend期间处于UNBLOCK
第二次WAIT()前:SIGUSR1是UNBLOCK的,因为已被第一次WAIT的SIG_SETMASK重置了
意味着后面四次sigsuspend前后的SIGUSR1都是UNBLOCK,for循环逻辑随时都会被SIGUSR1打断,不是预期的行为。不过楼主的输出困惑的地方是,进程被SIGUSR1干掉了,按说TELL_WAIT已经设置好信号处理函数了,不应该再有SIGUSR1的默认动作,。。。先按书中正确用法改下吧,确保每次suspend前后SIGUSR会被BLOCK。

论坛徽章:
0
5 [报告]
发表于 2014-06-09 09:52 |只看该作者
谢谢你的回复。
我最开始的那个问题已经解决了,signal()的语义有时候是BSD语义,这是一种可靠信号语义;有时候是System V语义,这是一种不可靠信号语义。对于前者,handler不用重新注册,对于后者,只捕捉一次,handler需要重新注册。用的哪种语义,一是依赖于OS平台,二是依赖于一些 feature_test_macros(7),比如gcc编译时加上 -D_GNU_SOURCE 之类的。
我这个代码,apue.h 中已经定义了几个这样的宏,使得 signal() 使用了 system V语义。一个简单的解决办法是 编译时加  -D_GNU_SOURCE。关于这点,可以参考 man 2 signal 的Portability部分。

第二个问题,就是竞争性问题,你说的确实是对的,我最开始没想到。apue书中提供的这个 TELL_WAIT/WAIT 模型,其实不适合于多次同步。我觉得一个解决办法是 把TELL_WAIT 中的那几行都搬到 WAIT() 中去,保证在每次 sigsuspend 前后,SIGUSR1都是处于block状态的。
回复 4# timespace


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP