信号处理程序中的取消点?

R..*_*R.. 6 c posix signals pthreads cancellation

如果程序调用函数作为信号处理程序的取消点会发生什么?POSIX指定了许多函数作为异步信号安全和取消点.如果一个信号处理程序调用了这样一个函数并且取消了,那么结果非常类似于线程启用异步取消时会发生什么 - 实际上更糟糕的是,因为所有的取消清理处理程序,可能都不是异步信号 - 安全,将从信号处理程序上下文调用.

在这种情况下,POSIX实际指定了什么,以及实现实际上做了什么?我在POSIX中找不到任何禁止信号处理程序中的取消点被采取行动的语言,也没有在glibc/nptl源中找到任何此类保护.

psm*_*ars 4

我不知道 POSIX 甚至敢于提及这个话题,但我还没有进行详尽的搜索。

对 gcc/nptl 系统的一些简短实验表明,正如我所怀疑的那样,我认为您也这样做了,NPTL 中没有这样的保护 - 取消处理程序确实从信号处理程序上下文中被调用。

下面的程序(对黑客行为等表示歉意)显示以下输出:

Signal handler called
Sent cancellation
Cleanup called
In sighandler
Run Code Online (Sandbox Code Playgroud)

...表明:

  • 信号处理程序被调用
  • 然后另一个线程调用pthread_cancel()
  • 然后取消处理程序被调用,而信号处理程序没有完成

这是程序:

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>

pthread_t mainthread;

int in_sighandler = 0;

void
cleanup (void *arg)
{
    write(1, "Cleanup called\n", strlen("Cleanup called\n"));
    if (in_sighandler) {
        write(1, "In sighandler\n", strlen("In sighandler\n"));
    } else {
        write(1, "Not in sighandler\n", strlen("In sighandler\n"));
    }
}


void
sighandler (int sig, siginfo_t *siginfo, void *arg)
{
    in_sighandler = 1;
    write(1,"Signal handler called\n", strlen("Signal handler called\n"));  // write() is a CP
    usleep(3000000); // usleep() is a CP; not strictly async-signal-safe but happens to be so in Linux
    write(1, "Signal handler exit\n", strlen("Signal handler exit\n"));
    in_sighandler = 0;
}

void *
thread (void *arg)
{
    sleep(1);
    pthread_kill(mainthread, SIGUSR1);
    usleep(500000);
    pthread_cancel(mainthread);
    printf("Sent cancellation\n");
    return (NULL);
}

int
main (int argc, char **argv)
{
    int rc;
    struct sigaction sa;
    pthread_t threadid;

    mainthread = pthread_self();

    // Set up a signal handler to test its cancellation properties
    sa.sa_sigaction = &sighandler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    rc = sigaction(SIGUSR1, &sa, NULL);
    assert(rc == 0);

    // Set up a thread to send us signals and cancel us
    rc = pthread_create(&threadid, NULL, &thread, NULL);
    assert(rc == 0);

    // Set up cleanup handlers and loop forever
    pthread_cleanup_push(&cleanup, NULL);
    while (1) {
        sleep(60);
    }
    pthread_cleanup_pop(0);
    return (0);
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,没错 - 尽管严格来说信号处理程序无法禁用取消(pthread 函数不是异步信号安全的)。该库*可以*阻止它创建的任何线程中的*所有*信号,但这并不理想(特别是因为在 Linux 上阻止 SIGRTMIN 会禁用取消......)。我一直认为取消是危险的 - 你不能从可取消的线程调用任何库函数,除非你确定库的设计考虑到了取消(否则它可能会分配资源,调用取消点函数,然后永远不会释放这些资源...) (3认同)
  • 如果没有更好的答案很快就会出现,我可能会接受你的。据我所知,这基本上意味着您不能在程序中使用取消,除非您的信号处理程序本身禁用取消或避免调用任何可能是取消点的函数。不幸的是,这反过来意味着在没有调用程序的意识/合作的情况下利用线程的库代码根本无法使用取消(因为调用程序可能已经设置了信号处理程序);任何使用都可能导致竞争条件,信号处理程序被取消。 (2认同)