4

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

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

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

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

换句话说-一旦开始,信号处理程序就会执行而不会被以下内容中断:

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

3 回答 3

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
于 2013-03-27T04:38:55.643 回答
4

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

查看手册页

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

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

参考:

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

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

  • signal()

    signal() 的唯一可移植用途是将信号的处置设置为 SIG_DFL 或 SIG_IGN。使用 signal() 建立信号处理程序时的语义因系统而异(POSIX.1 明确允许这种变化);不要将其用于此目的。

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

    [...] 此外,相同信号的快速传递可能会导致处理程序的递归调用。

于 2013-03-27T04:43:10.277 回答
4

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

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

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

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

于 2013-03-27T04:44:16.790 回答