Linux:信号处理程序执行是否可以被抢占?

时间:2013-03-27 04:33:43

标签: c linux signals interrupt-handling

我遇到了以下信号处理程序代码,它存储了errno变量,因此它不会影响主线程的errno处理。

void myhandler(int signo)
{
    int esaved;
    esaved = errno;
    write(STDOUT_FILENO, "Got a signal\n", 13);
    errno = esaved;
}

但这真的有用吗?如果另一个线程在write()之后和恢复errno之前检查共享errno变量会发生什么?由于竞争条件,该线程是否会出错?错误值

或者信号处理程序相对于线程/进程执行原子,这样一旦信号处理程序执行,内核就不会调度线程直到信号处理程序完成?

换句话说 - 一旦启动,执行信号处理程序而不会被中断:

 - 1) Scheduler (process/threads),  or  
 - 2) Other signals,  or
 - 3) Hardware interrupt handlers  ?

3 个答案:

答案 0 :(得分:4)

变量errno是特定于线程的 - 或者更准确地说,在线程环境中,是线程本地或每线程的值 - 因此在此线程中对errno执行的操作获胜在其他线程中影响errno

代码保存和恢复errno的目的是隐藏write()myhandler()系统调用设置的任何错误。但是,如果write()失败,它可能会将errno设置为某个新值 - 它不会为零,但这就是您所能说的全部 - 但您要求的代码会从中恢复在调用write()之后调用write()之前,所以写入发生的事实是“不可见的”,因为它不会影响此线程的errno

信号处理函数本身可能被信号屏断,信号掩码不会被信号屏蔽。它也可以重新安排。硬件中断也可能发生,但代码将难以注意到这些影响。


在Linux上,您可能会发现/usr/include/bits/errno.h定义了宏errno(包含在更多#ifdef代码中,而不是此处显示的内容):

extern int *__errno_location (void) __THROW __attribute__ ((__const__));

#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif

答案 1 :(得分:4)

信号处理程序确实可以被另一个信号中断(假设它与首先调用处理程序的信号不同)。

  

您的处理程序仍然可以通过传递另一种信号来中断。为避免这种情况,您可以使用传递给sigaction的操作结构的sa_mask成员来明确指定在信号处理程序运行时应阻止哪些信号。这些信号是调用处理程序的信号以及通常被进程阻塞的任何其他信号的补充。请参阅阻止处理程序。

     

当处理程序返回时,阻塞信号集将恢复为处理程序运行之前的值。因此,在处理程序中使用sigprocmask只影响在处理程序本身执行期间可以到达的信号,而不是处理程序返回后可以到达的信号。

http://www.gnu.org/software/libc/manual/html_node/Signals-in-Handler.html#Signals-in-Handler

答案 2 :(得分:3)

在Linux上,errno是一个宏,它扩展为一个函数调用,返回一个可修改的左值,它对每个线程都是不同的。

查看man page

  

errno由ISO C标准定义为类型的可修改左值          int,并且不得显式声明; errno可能是一个宏。 errno是          本地线程;在一个线程中设置它不会影响它的任何值          其他线程。

然而,确实可以在信号处理程序执行期间再次触发信号(如果您使用signal()而不是sigaction(),这取决于您的环境;这些不一致是sigaction()的原因建议改为),或者其他信号可以中断处理程序的执行。这就是为什么通常在设置信号处理时,使其阻止信号(s,在多个信号的情况下使用该处理程序) - 通过将其添加到信号掩码 - 在执行信号处理程序期间。这可以防止信号处理程序自行中断(在某些情况下,防止无限循环)。

参考文献:

  • sa_mask组件可以传递给sigaction()

      

    sa_mask指定应该阻止的信号掩码(即添加到      调用信号处理程序的线程的信号掩码)      在执行信号处理程序期间。另外,信号哪个      除非使用SA_NODEFER标志,否则触发处理程序将被阻止

  • signal()

      

    signal()的唯一便携式用途是设置信号的配置      SIG_DFL或SIG_IGN。使用signal()建立一个语义时的语义      信号处理程序因系统而异(POSIX.1明确允许这样做      变异); 请勿将其用于此目的。

         

    POSIX.1通过指定sigaction(2)解决了可移植性问题      在信号处理程序时提供语义的显式控制      调用;使用该接口而不是signal()。

         

    [...]此外,同一信号的快速交付可以      导致处理程序的递归调用。

相关问题