问题描述
使用select()与管道 - 这是我正在做的,现在我需要捕获SIGTERM.我该怎么做? select()返回错误(<0)时,我必须做到吗?
推荐答案
首先,如果未捕获,SIGTERM将 kill 您的进程,并且select()将不是返回.因此,必须为SIGTERM安装信号处理程序.使用sigaction().
然而,SIGTERM信号可以在螺纹 not 在select()上堵塞的那一刻到达.如果您的进程大多睡在文件描述符中,这将是一种罕见的条件,但它可以发生.这意味着您的信号处理程序必须做点什么通知中断的主例程,即设置一些标志变量(类型sig_atomic_t类型),或者您必须保证只在进程睡觉时提供SIGTERM select().我会采用后一种方法,因为它更简单,尽管灵活(见帖子的结尾).
所以,只要在调用select()之前,您就会阻止SIGTERM,并在函数返回后立即重新锁定它,以便您的进程仅在睡在select()内时收到信号.但请注意,这实际上创造了竞争条件.如果信号在取消阻止之后到达,但只需在select()之前,系统调用尚未调用,因此它不会返回-1.如果信号在select()成功返回之后,但只是在重新块之前,您也丢失了信号.
因此,您必须使用pselect().它是围绕select()原子的阻塞/解锁.
首先,在进入pselect()环之前,使用sigprocmask()块SIGTERM.之后,只需调用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&A指向的评论之一;
>中断将导致select()与errno设置为eIntr
返回-1是;对于任何中断(信号)捕获,选择将返回,并且ertno将设置为eIntr.
现在,如果您特别想捕获sigterm,那么您需要将其设置为呼叫signal,如此;signal(SIGTERM,yourcatchfunction);
应该定义捕获功能,如
void yourcatchfunction(int signaleNumber) { .... }所以总之,您已经设置了信号处理程序yourcatchfunction,您的程序当前处于<C1>呼叫等待IO - 当信号到达时,将调用您的CatchFunction并从中返回时呼叫将使用ertrno返回到eIntr. 但是,请注意,Sigterm可以随时发生所以您可能 not 在发生时,在某个情况下,您将永远不会看到eIntr但只有常规呼叫yourcatchfunction 因此,使用ERR和ERRNO EINTR返回的SELECT()就是这样,您可以采取非阻塞操作 - 这不是将捕获信号.
其他推荐答案
您可以在循环中调用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(). */ }