int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

中的
           struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

sa_flags 各参数作用

1.SA_NODEFER 作用:
                  Do  not  prevent  the  signal  from  being received from within its own signal handler.  This flag is only meaningful when establishing a signal handler.
                  SA_NOMASK is an obsolete, non-standard synonym for this flag.

个人理解:设置了该参数,就是允许 当前正在处理 该信号的 信号函数中,可以继续接受并处理该信号。 
示例参见 :http://stackoverflow.com/questions/2418812/catching-signal-inside-its-own-handler


  1. #include<stdio.h>
  2. #include<signal.h>

  3. void handler(int signo)
  4. {
  5.     printf("Into handler\n");
  6.     while(1);
  7. }
  8. int main()
  9. {
  10.     struct sigaction act;
  11.     act.sa_handler = handler;
  12.     act.sa_flags = 0;
  13.     sigemptyset(& act.sa_mask);
  14.     sigaction(SIGINT, &act, NULL);
  15.     while(1);
  16.     return 0;
  17. }

如上代码:13行中sa_flags啥标记都没设置

[root@localhost c]# gcc signal_SA_NODEFER.c 
[root@localhost c]# ./a.out 
^CInto handler
^C^C^C^C^C^C

注意只有第一次按CTRL+C 时,打印了Into handler,而之后的怎么按ctrl+C都没反应
而该信号函数正在执行while(1); 就忽略了新收到的信号。

如下设置
act.sa_flags |= SA_NODEFER;
之后的行为,则可以在处理信号函数的过程中继续接受并处理该信号,可以看到每按一次CTRL+C就可以print 一次Into handler

  1. [root@localhost c]# cat signal_SA_NODEFER.c
  2. #include<stdio.h>
  3. #include<signal.h>

  4. void handler(int signo)
  5. {
  6.     printf("Into handler\n");
  7.     while(1);
  8. }
  9. int main()
  10. {
  11.     struct sigaction act;
  12.     act.sa_handler = handler;
  13.     act.sa_flags |= SA_NODEFER;
  14.     sigemptyset(& act.sa_mask);
  15.     sigaction(SIGINT, &act, NULL);
  16.     while(1);
  17.     return 0;
  18. }
  19. [root@localhost c]# gcc signal_SA_NODEFER.c
  20. [root@localhost c]# ./a.out
  21. ^CInto handler
  22. ^CInto handler
  23. ^CInto handler
  24. ^CInto handler
当然:一般信号函数中仅仅设置一个flag,然后直接返回,而在程序的主loop中去处理这个flag对于的操作

如:

  1. #include<stdio.h>
  2. #include<signal.h>

  3. static volatile do_print = 0;

  4. void handler(int signo)
  5. {
  6.     do_print = 1;
  7. }
  8. int main()
  9. {
  10.     struct sigaction act;
  11.     act.sa_handler = handler;
  12.     act.sa_flags = 0;
  13.     sigemptyset(&act.sa_mask);
  14.     sigaction(SIGINT, &act, NULL);
  15.     while(1)
  16.     {
  17.         if (do_print)
  18.         {
  19.             printf("Into handler\n");
  20.             do_print = 0;
  21.         }
  22.     }
  23.     return 0;
  24. }
执行结果:
[root@localhost c]# ./a.out 
^CInto handler
^CInto handler
^CInto handler

可以看到 这样即使不设置act.sa_flags |= SA_NODEFER 也可以得到期望的结果,
为啥呢

主要还是 该信号处理函数非常简单,程序在收到下一次的ctrl+c信号时,上个已经执行完毕了,所以不加也没关系,
但如果信号函数中要处理一系列的流程,最好还是加上,比如 服务器程序在收到SIGSEGV,或SIGBUS 等异常时,程序需要在信函函数中将异常的堆栈信息dump到日志中时,最好还是加上,这样可以尽可能多的将堆栈dump出来用来查错,这部分可以参照squid中对信号的处理。

另外:为啥信号函数中一般只是设置个flag 就返回呢?

这里面常见的一个故障就是:信号的到来是不确定的,而好多函数是可以被信号打断的,
比如在当前程序 正在调用malloc 分配一片内存(还尚未执行完),突然来了个信号被打断了,去执行信号函数了,而信号函数中又去调用了一次malloc,完了,死锁了。。

如:
[root@localhost ]# gstack 9277
#0  0x0000003cbd8f0dfe in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x0000003cbd87c1e8 in _L_lock_9162 () from /lib64/libc.so.6
#2  0x0000003cbd879ae2 in malloc () from /lib64/libc.so.6
#3  0x0000003cbd87fe62 in strndup () from /lib64/libc.so.6
#4  0x0000000000406ee4 in str_strs(char*, char, char***) ()
#5  0x0000000000404100 in sigcatcher(int, int, sigcontext*) ()
#6  
#7  0x0000003cbd875812 in malloc_consolidate () from /lib64/libc.so.6
#8  0x0000003cbd8786c2 in _int_malloc () from /lib64/libc.so.6
#9  0x0000003cbd87aab6 in _int_realloc () from /lib64/libc.so.6
#10 0x0000003cbd87ad65 in realloc () from /lib64/libc.so.6
#11 0x00000000004043d6 in main ()

main中第10行正在realloc 在第6行中被打断,然后在信号处理函数中 又执行了一次 malloc,出现了死锁。(此处怀疑应该是malloc分配时必然存在一个全局的锁)

这部分apue信号处理的章节有详述。




02-02 21:48