linux 信号处理

曾做过signal相关的一点儿开发,谈谈我的一些理解。

首先,需要理解几个signal相关的函数。
  sigaddset(sigset_t* sigSet, int sigNum ) :  将信号sigNum 添加到信号集 sigSet 中;
  sigdelset(sigset_t* sigSet, int sigNum) : 将信号 sigNum 从信号集 sigSet 中删除;
  sigemptyset(sigset_t* sigSet) : 清空信号集;
  sigfillset(sigset_t* sigSet) : 在信号集中打开所有的信号。

但是这个时候只是定义好了如此一个信号集,还有对信号的操作函数:
   pthread_sigmask(int opCode, sigset_t* sigSet, sigset_t* oldSigSet) : opCode 指定了如何对 sigSet 里的信号进行处理。opCode 有三个值: SIG_BLOCK (将sigSet中的信号加到当前线程的屏蔽集中),SIG_UNBLOCK (将sigSet 中的信号从当前线程屏蔽集中删除),SIG_SETMASK (将sigSet 设为当前线程的屏蔽集)。 若oldSigSet != NULL,则将之前的信号屏蔽集存入其中。
   另外还有个函数 sigprocmask() 也有类似功能。区别是:pthread_sigmask() 是线程库函数,用于多线程进程。sigprocmask() 是旧的实现,用于单线程的进程。

  sigwait(sigset_t* sigSet, int* sigNum) : 当前线程等待 sigSet 中的信号。没有捕获到信号时,线程挂起;当捕获到时,函数返回,并将信号值存入 sigNum。
  sigaction(int sigNum, sigaction* newAct, sigaction* oldAct) :  捕获信号 sigNum,并调用相应的处理函数(定义在 newAct 中)。
  
虽然sigwait() 和 sigaction() 都是用于捕获信号,但两者还是有较大区别:sigwait() 是阻塞的,线程会一直挂起直到捕获到信号,并且对信号的处理是定义在 sigwait()后的,只会在当前线程内执行;而sigaction()是非阻塞的,当信号被捕获时,会由进程内当前被调度到的线程来执行处理函数(好 像是,not very sure...),被哪个线程处理是随机的。
所以,sigaction()适用于对实时性要求很高的时候。而在普通情况下建议使用sigwait(),因为其具有较好的可控性。
另外,还需要注意的是,SIG_KILL(大家应该都用过kill -9 吧) 和 SIG_STOP 是不能被用户屏蔽或捕获的。

好了,当理解了这几个函数后,可以自己试着来对信号进行处理了。对于lz的需求,简单举例如下:
  sigset_t blockSet, waitSet;
  int sigNum;
  
  sigfillset(&blockSet);     // open all signals in blockSet
  pthread_sigmask(SIG_BLOCK, blockSet, NULL);  //block all signals (except SIG_KILL & SIG_STOP also) in current thread
  
  sigemptyset(&waitSet);     // empty all signals in waitSet
  sigaddset(&waitSet, SIG_XXX);    // a signal wanted to be captured
  sigaddset(&waitSet, SIG_YYY);    // another signal wanted to be captured
  
  if ( sigwait(&waitSet, &sigNum) != 0 )    // wait for wanted signals
  {
    perror("Error - sigwait() is failure!/n");
    exit(1);
  }
  
  switch (sigNum)
  {
    case SIG_XXX:
      printf("SIG_XXX is captured!/n");
      break;
   
    case SIG_YYY:
      printf("SIG_YYY is captured!/n");
      break;
      
    default:
      printf("Error - cannot reach here!/n");
      exit(1);
  }
  
  return OK;

另外,用 sigaction() 也可以实现。

 

Ps  Kernel 2.6.22 加入的 signalfd 让 signal handling 有了新办法。

signal 处理是 Unix 编程的难点,因为 signal 是异步的,而且发生在“当前线程”里,会遇到“可重入”的难题。其实“线程”是 1993 才加入到 Unix 中,之前的 20 多年根本就没有“主线程”一说,我这里的意思是 signal handler 是像 coroutine 一样被调用的,而不是通常的 subroutine。Raymond Chen 有一 篇文章 谈到了这个问题。

在 Unix/Linux 支持线程以后,signal 就更难处理了,规则变得晦涩(想想 signal delivery 的对象)。而且它不符合“every thing is a file” 的 Unix 哲学,不能把 signal 事件当成文件来读。不过 2.6.22 加入的 signalfd 让事情有了转机,程序能像处理文件一样处理 signal,可以 read,也可以 select/poll/epoll,能融入标准的 IO multiplexing 框架中,而不需要在程序里另外用一对 pipe 来把 signal 转为 IO event。(libev 似乎是这么做的,另外还有 GHC http://hackage.haskell.org/trac/ghc/ticket/1520

这下多线程程序与 signals 打交道容易多了,一个 event loop 就能搞定 IO 和 timer 和 signals,完美。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值