用select()从管道读取时捕获信号

时间:2011-08-05 20:19:57

标签: c signals pipe signal-handling

using select() with pipe - 这就是我正在做的事情,现在我需要抓住SIGTERM。我该怎么做?当select()返回错误(< 0)时,我是否必须这样做?

3 个答案:

答案 0 :(得分:16)

首先,如果没有抓住,SIGTERM终止您的流程,而select()返回。因此,您必须为SIGTERM安装信号处理程序。使用sigaction()

执行此操作

但是,SIGTERM信号可能会在select()处阻止 的线程时到达。如果您的进程主要是在文件描述符上休眠,那么这将是一种罕见的情况,但否则会发生。这意味着您的信号处理程序必须执行某些操作以通知主例程中断,即设置一些标志变量(类型为sig_atomic_t),或者您必须保证SIGTERM仅在进程正在select()上暂停。

我将采用后一种方法,因为它更简单,虽然不太灵活(见帖子的结尾)。

因此,您在调用SIGTERM之前阻止select(),并在函数返回后立即重新锁定它,以便您的进程仅在select()内睡眠时接收信号。但请注意,这实际上会产生竞争条件。如果信号在解除阻塞之后到达,但在调用select()之前,系统调用尚未被调用,因此它不会返回-1。如果信号在select()成功返回后到达,但在重新阻止之前,您也丢失了信号。

因此,您必须使用pselect()。它以原子方式select()进行阻塞/解除阻塞。

首先,在进入SIGTERM循环之前使用sigprocmask()阻止pselect()。之后,只需使用pselect()返回的原始掩码调用sigprocmask()。这样,您就可以保证只有在select()上睡觉时才会中断您的流程。

总结:

  1. SIGTERM安装处理程序(无效);
  2. 在输入pselect()循环之前,使用SIGTERM阻止sigprocmask();
  3. 使用pselect();
  4. 返回的旧信号掩码调用sigprocmask()
  5. pselect()循环中,现在您可以安全地检查pselect()是否返回-1 并且 errnoEINTR。< / LI>

    请注意,如果在pselect()成功返回后,您执行了大量工作,则在响应SIGTERM时可能会遇到更长的延迟(因为该流程必须执行所有处理并返回{{ 1}}在实际处理信号之前)。如果这是一个问题,您必须在信号处理程序中使用标志变量,以便您可以在代码中的许多特定点检查此变量。但是,使用标志变量并不能消除竞争条件,也不会消除pselect()的需要。

    请记住:每当您需要等待一些文件描述符来传递信号时,必须使用{{1} }(或pselect(),对于支持它的系统。)

    编辑:没有什么比代码示例更能说明用法了。

    pselect()

答案 1 :(得分:4)

答案部分在于您指出的Q&amp; A中的一条评论;

&GT;中断将导致select()返回-1,并将errno设置为EINTR

那是;对于捕获的任何中断(信号),select将返回,并且errno将被设置为EINTR。

现在,如果您特别想要捕获SIGTERM,那么您需要通过调用signal来设置它,就像这样;

signal(SIGTERM,yourcatchfunction);

你的catch函数应该被定义为

void yourcatchfunction(int signaleNumber) { .... }

总而言之,您已经设置了一个信号处理程序yourcatchfunction,并且您的程序当前处于等待IO的select()调用中 - 当信号到达时,您的catch函数将被调用,当您返回时从那里,select调用将返回,并将errno设置为EINTR。

但请注意,SIGTERM可以随时发生,因此当发生时,可能会处于选择调用中,在这种情况下,您将永远不会看到EINTR但只能定期调用yourcatchfunction

因此,使用err和errno EINTR返回的select()只是为了你可以采取非阻塞动作 - 它不会捕获信号。

答案 2 :(得分:1)

您可以循环调用select()。这称为重新启动系统调用。这是一些伪C。

int retval = -1;
int select_errno = 0;

do {
   retval = select(...);

   if (retval < 0)
   {
       /* Cache the value of errno in case a system call is later
        * added prior to the loop guard (i.e., the while expression). */
       select_errno = errno;
   }

   /* Other system calls might be added here.  These could change the
    * value of errno, losing track of the error during the select(),
    * again this is the reason we cached the value.  (E.g, you might call
    * a log method which calls gettimeofday().) */

/* Automatically restart the system call if it was interrupted by
 * a signal -- with a while loop. */
} while ((retval < 0) && (select_errno == EINTR));

if (retval < 0) {
   /* Handle other errors here. See select man page. */
} else {
   /* Successful invocation of select(). */
}