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

发布于 2024-10-03 11:04:44 字数 267 浏览 3 评论 0原文

如果程序调用作为信号处理程序取消点的函数,会发生什么情况? POSIX 指定了许多函数作为异步信号安全点和取消点。如果信号处理程序调用这样的函数并执行取消操作,则结果与线程启用异步取消时发生的情况非常相似 - 实际上更糟糕,因为所有取消清理处理程序(可能不是异步信号)安全,将从信号处理程序上下文中调用。

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

What happens if a program calls a function which is a cancellation point from a signal handler? There are a number of functions which POSIX specifies as both async-signal-safe and cancellation points. If a signal handler calls such a function and cancellation is acted upon, the result is quite similar to what would happen if the thread had enabled asynchronous cancellation - actually much worse, because all the cancellation cleanup handlers, which are probably not async-signal-safe, would be called from a signal-handler context.

What does POSIX actually specify in this case, and what do implementations actually do? I can't find any language in POSIX that would forbid cancellation points in signal handlers from being acted upon, nor any such protection in the glibc/nptl source.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

零度° 2024-10-10 11:04:44

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

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

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

Signal handler called
Sent cancellation
Cleanup called
In sighandler

... 表明:

  • 信号处理程序被调用
  • 另一个线程,然后调用 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);
}

I'm not aware that POSIX even dares to mention this topic, but I haven't done an exhaustive search.

Some brief experimentation with a gcc/nptl system reveals that, as I suspected and I think you did too, there is no such protection in NPTL - the cancellation handlers do indeed get called, from within the signal handler context.

The program below (apologies for the hackiness etc) displays the following output:

Signal handler called
Sent cancellation
Cleanup called
In sighandler

... indicating that:

  • the signal handler got called
  • the other thread then called pthread_cancel()
  • the cancellation handler then got called, without the signal handler completing

Here's the program:

#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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文