C Linux:信号处理程序的执行可以被抢占吗?
我遇到了以下存储errno变量的信号处理程序代码,这样它就不会影响主线程的errno处理C Linux:信号处理程序的执行可以被抢占吗?,c,linux,signals,interrupt-handling,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变量,会发生什么情况?由于争用条件,该线程是
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 ?
变量
errno
是特定于线程的-或者更准确地说,在线程环境中是线程本地值或每线程值-因此在此线程中对errno
所做的操作不会影响其他线程中的errno
代码保存和还原errno
的目的是隐藏write()
系统调用在myhandler()
中设置的任何错误。但是,如果write()
失败,它可能会将errno
设置为某个新值-它不会为零,但这就是您所能说的-但是您询问的代码会在调用write()
后从调用write()
之前恢复该值,因此,发生写入的事实是“不可见的”,即它不会影响此线程的errno
信号处理器功能本身可能会被未被其响应信号的信号掩码阻止的信号中断。也可以重新安排。硬件中断也可能发生,但代码将很难注意到这些影响
在Linux上,您可能会发现定义宏的
/usr/include/bits/errno.h
(包含在比此处所示更多的#ifdef
代码中):
在Linux上,
errno
是一个宏,它扩展为一个函数调用,返回一个可修改的左值,该左值对于每个线程都是不同的
请查看:
ISO C标准将errno定义为类型为的可修改左值
int,并且不能显式声明;errno可能是一个宏。厄尔诺是
线程本地;在一个线程中设置它不会影响它在任何线程中的值
其他线程
但是,实际上,在执行信号处理程序的过程中可以再次触发信号(如果您使用了signal()
而不是sigaction()
,这取决于您的环境;这些不一致性是建议改为使用sigaction()
的原因),或者另一个信号可以中断处理程序的执行。这就是为什么通常在设置信号处理程序时,通过在信号处理程序执行期间将其添加到信号掩码,使其阻塞信号。这可以防止信号处理程序中断自身(在某些情况下,可以防止无限循环)
参考资料:
可传递到以下位置的组件: sa_mask指定了一个应被阻止的信号掩码(即,添加到 调用信号处理程序的线程的信号掩码) 在执行信号处理程序期间。此外,信号 除非使用SA_NODEFER标志,否则处理程序将被阻止sa_mask
- : signal()的唯一可移植用途是将信号的配置设置为 信号灯或信号灯。使用signal()建立 信号处理程序因系统而异(POSIX.1明确允许这样做) 变异)请勿将其用于此目的。 POSIX.1通过指定sigaction(2)解决了可移植性混乱问题,它 在调用信号处理程序时提供语义的显式控制 援引;使用该接口而不是signal() [……]此外,快速传递同样的信号可能会 导致处理程序的递归调用
即使指令
++
也不是原子指令。也不要在侧信号处理程序中使用write()
函数。在side signal Handler中,信号处理程序中有OK函数(请参阅),并且write()
是异步信号安全的。POSIX比C标准自由得多。不,信号处理程序不会阻止其他线程运行。这包括执行自己的信号处理程序。
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