using select() with pipe - 这就是我正在做的,现在我需要赶上SIGTERM
。我该怎么做?当select()
返回错误(< 0)时,我必须这样做吗?捕获信号,同时从管道读取select()
回答
首先,SIGTERM
将杀死你的过程中,如果不抓住,select()
将不回报。因此,您必须安装SIGTERM
的信号处理程序。这样做使用sigaction()
。
然而,SIGTERM
信号可以在瞬间到达您的线程不受阻于select()
。如果你的进程大部分是在文件描述符上休眠的话,这将是一种罕见的情况,但它可能会发生。这意味着您的信号处理器必须做一些事情来通知中断的主程序,即设置一些标志变量(类型sig_atomic_t
),或者您必须保证仅在select()
处于睡眠状态时才会传送。
我会采用后一种方法,因为它更简单,尽管不够灵活(请参阅帖子末尾)。
所以,你只是打电话select()
之前阻止SIGTERM
,并reblock它在函数返回之后马上让你的进程只接收而内select()
睡觉的信号。但请注意,这实际上会造成竞争状况。如果信号在解锁后立即到达,但在select()
被调用之前,系统调用将不会被调用,因此它不会返回-1
。如果信号刚刚在select()
返回成功后到达,但在重新阻止之前,您也丢失了信号。
因此,您必须使用pselect()
。它以原子方式围绕select()
进行阻塞/解锁。
首先,块SIGTERM
使用sigprocmask()
在进入pselect()
循环之前。之后,请致电pselect()
,并带上sigprocmask()
返回的原始面具。这样你保证你的过程只会在select()
上睡觉时被打断。
总结:
- 安装一个处理程序
SIGTERM
(什么都不做); - 在进入
pselect()
循环之前,使用sigprocmask()
的块SIGTERM
; - 调用
pselect()
用旧的信号掩码返回sigprocmask()
; - 里面的
pselect()
循环,现在你可以检查是否安全返回pselect()
-1
和errno
是EINTR
。
请注意,如果成功后pselect()
回报,你做了很多的工作,你可以回答SIGTERM
时(因为过程必须尽一切处理和实际处理信号之前返回pselect()
)经历更大的延迟。如果这是一个问题,您必须在信号处理程序中使用标志变量,以便您可以在代码中的许多特定点检查此变量。但是,使用标志变量并不能消除竞争条件,并且不会消除对pselect()
的需求。
记住:每当你需要等待一些文件描述符或用于信号的传递,你必须使用pselect()
(或ppoll()
,为支持它的系统)。
编辑:没有比代码示例更好地说明使用情况。
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <unistd.h>
// Signal handler to catch SIGTERM.
void sigterm(int signo) {
(void)signo;
}
int main(void) {
// Install the signal handler for SIGTERM.
struct sigaction s;
s.sa_handler = sigterm;
sigemptyset(&s.sa_mask);
s.sa_flags = 0;
sigaction(SIGTERM, &s, NULL);
// Block SIGTERM.
sigset_t sigset, oldset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGTERM);
sigprocmask(SIG_BLOCK, &sigset, &oldset);
// Enter the pselect() loop, using the original mask as argument.
fd_set set;
FD_ZERO(&set);
FD_SET(0, &set);
while (pselect(1, &set, NULL, NULL, NULL, &oldset) >= 0) {
// Do some processing. Note that the process will not be
// interrupted while inside this loop.
sleep(5);
}
// See why pselect() has failed.
if (errno == EINTR)
puts("Interrupted by SIGTERM.");
else
perror("pselect()");
return EXIT_SUCCESS;
}
答案部分在您指出的Q &的评论之一中;
>中断将导致选择()返回一个-1,并将errno设置为EINTR
即;对于捕获的任何中断(信号),select将返回,并且errno将被设置为EINTR。
现在,如果你特别想捕捉SIGTERM,那么你需要设置一个signal
的电话,就像这样;
signal(SIGTERM,yourcatchfunction);
在您捕捉功能应该因此,在总结中定义类似
void yourcatchfunction(int signaleNumber) { .... }
,你必须设置一个信号处理器yourcatchfunction
,目前你的程序是在select()
呼叫等待IO - 当信号到达,你的catchfunction将被调用,当你从那返回时,select调用将返回errno设置为EINTR。
但是要注意的是,SIGTERM可以随时发生所以你可能不在select调用发生时,在这种情况下,你将永远不会看到EINTR但只有yourcatchfunction
因此,select()以err和errno EINTR返回就是这样,您可以采取非阻塞操作 - 它不会捕获信号。
您可以在循环中调用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(). */
}
- 1. select() - 在管道上阻塞后读取
- 2. 从管道读取时无限循环
- 3. 从管道读取时,Win32 ReadFile挂起
- 4. 从管道读取STDIN时获取用户输入?
- 5. 写入管道信号同步
- 6. 从管道读取C
- 7. Jenkins管道从yaml读取
- 8. 从管道读取SPI
- 9. 从管道问题读取
- 10. 从管道读取阻塞
- 11. 从管道读取错误
- 12. 如何获取信号捕获SIGABRT
- 13. Golang捕获信号
- 14. 如何从QAudioInput捕获两个通道的音频信号
- 15. 2个孩子写信给管道,家长从管道中读取C
- 16. 使用管道时ruby捕获输入
- 17. Jenkins管道和信号灯
- 18. 捕获管道值(Noob Question)
- 19. fread超时'mod_fcgid:读取超时管道'
- 20. 如何从Q(双)spinbox捕获信号
- 21. 进程如何知道何时从管道读取
- 22. 从uiwebview获取信号
- 23. 如何更改'管道核心转储到程序'捕获的信号列表?
- 24. posix_memalign是否捕获信号?
- 25. 如何捕获锈信号
- 26. 捕获SIGVTALRM信号,cp
- 27. 捕获传入的信号
- 28. 在linux中捕获信号
- 29. Android:捕获SIGSEGV信号JNI
- 30. Python - 简单的读管道从管道
@Akek:首先,谢谢你的详细解释。我有几个问题:SIGTERM信号可以在你的线程在select()处不被阻塞的时刻到达 - 如何处理SIGTERM?寻求来自fds的输入是我的过程所做的事情之一(不是唯一的事情)。 – hari
@hari:如果你这样做,信号将不会在这种情况下传递。如果它到达'pselect()'之外,那么信号只有在你调用'pselect()'的时候才被延迟和传递。而且,推迟信号以供后续处理没有任何不妥之处。如果您想尽可能快地处理信号,您将不得不使用我简要描述的标记方法。而不是只设置一个标志,你实际上可以在信号处理程序中做更多的处理,但这是危险的:你仅限于调用异步安全函数,并且全局状态可能不一致。 – alecov
谢谢。顺便说一句,在某些时候,我确实需要'SIG_UNBLOCK'阻止的信号,对吧? – hari