【问题标题】:Signal handler terminates program信号处理程序终止程序
【发布时间】:2019-03-06 16:35:31
【问题描述】:

我尝试使用 sigaction 安装信号处理程序,然后像这样在单个线程上调用它:

void
my_signal_handler ( int signo, siginfo_t *info, void *extra )
{
   printf("my signal handler\n" );
}


int threadsupervisor() {

<...>

struct sigaction action;
struct sigaction oldHandler;

action.sa_flags = SA_SIGINFO;
action.sa_sigaction = my_signal_handler;

sigaction(SIGRTMIN + 3, &action, &oldHandler );

// send signal to affected thread
pthread_kill( threadId, SIGRTMIN + 3 );

// restore original signal handler
sigaction( SIGRTMIN + 3, &oldHandler, NULL );
}

线程确实收到 SIG37,然后整个应用程序终止。信号处理程序完成后程序/线程不应该继续吗?

问候

【问题讨论】:

  • 你在valgrind下运行代码了吗?它应该会给你一些提示。
  • printf() 在信号处理程序中是未定义的行为。您不能安全地从信号处理程序调用库函数。每footnote 188 of the C standard:“因此,信号处理程序通常不能调用标准库函数。” POSIX allow the use of async-signal-safe functions 可以从信号处理程序中调用。 printf() 不是其中之一。
  • 您没有初始化action 或设置它的所有成员。初始化会更容易和更清洁。如果没有其中之一,您至少还会从系统尝试使用action.sa_mask 中获得未定义的行为。
  • 程序应该继续的初始假设是否正确?还是我必须提出 SIGCONT 才能继续?

标签: c linux


【解决方案1】:

主要问题是restore original signal handler执行得太早了。

我写了一个SystemTap脚本来追踪do_sigactiondo_signal,如下:

probe begin {
    printf("start.\n");
}

probe kernel.function("do_signal") {
    if ("test_sigaction" == execname()) {
        printf("do_signal pid=%d\n", pid());
    }
}

probe kernel.function("handle_signal") {
    if ("test_sigaction" == execname()) {
        printf("handle_signal pid=%d\n", pid());
    }
}

probe kernel.function("do_sigaction").return {
    if ("test_sigaction" == execname()) {
        printf("do_sigaction: ret=%d sig=%d act=%p oact=%p pid=%d\n", 
            $return,  @entry($sig), @entry($act),
                @entry($oact), pid());
    }
}

结果是

do_sigaction: ret=0 sig=37 act=0xffffc90006ccbec8 oact=0xffffc90006ccbee8 pid=45920 //STEP1
do_sigaction: ret=0 sig=37 act=0xffffc90006ccbec8 oact=0x0 pid=45920 //STEP2
do_signal pid=45920 //STEP3
do_signal pid=45920

原因:

  • STEP1:在主线程中将信号处理程序设置为 my_signal_handler
  • STEP2:在主线程中恢复原始信号处理程序
  • STEP3:do_signal 在子线程返回时执行 内核模式到用户模式。

显然这里存在并发问题。 可以在执行 do_signal 之前恢复信号处理程序。 在恢复信号处理程序之前需要并发控制,或者将恢复处理程序移动到 my_signal_handler 函数,如:

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

struct sigaction oldHandler;
void my_signal_handler(int signo, siginfo_t *info, void *extra)
{
    printf("my signal handler\n" );
    // restore original signal handler
    sigaction( SIGRTMIN + 3, &oldHandler, NULL );
}

int threadsupervisor(pthread_t thread_id)
{
    struct sigaction action;

    memset(&action, 0, sizeof(action));
    action.sa_flags = SA_SIGINFO;
    action.sa_sigaction = my_signal_handler;

    sigaction(SIGRTMIN + 3, &action, &oldHandler );

    // send signal to affected thread
    pthread_kill(thread_id, SIGRTMIN + 3 );
    return 0;
}

void *test_thread(void *args)
{
    long loop = 0;
    while(1) {
        printf("sleep %ld\n", ++loop);
        sleep(1);
    }
    return (void *)NULL;
}

int main()
{
    pthread_t thread_id;
    pthread_create(&thread_id, NULL, test_thread, NULL);
    threadsupervisor(thread_id);
    pthread_join(thread_id, NULL);
    return 0;
}

【讨论】:

    猜你喜欢
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    • 2014-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-23
    相关资源
    最近更新 更多