使用SELECT()从管道读取时捕捉信号[英] catching signals while reading from pipe with select()

本文是小编为大家收集整理的关于使用SELECT()从管道读取时捕捉信号的处理方法,想解了使用SELECT()从管道读取时捕捉信号的问题怎么解决?使用SELECT()从管道读取时捕捉信号问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

使用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().

总结:

  1. 为SIGTERM安装一个处理程序(无做);
  2. 在进入pselect()环路之前,使用sigprocmask()块SIGTERM;
  3. 呼叫pselect()与sigprocmask()返回的旧信号掩码;
  4. 在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(). */
}

本文地址:https://www.itbaoku.cn/post/359432.html

相关标签/搜索