带有长信号处理程序(SIGALARM)的计时器的问题 [英] Issue with timer with long signal handler (SIGALARM)

查看:266
本文介绍了带有长信号处理程序(SIGALARM)的计时器的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一个计时器,每1秒发出一个信号SIGALARM.睡觉的信号处理器 记录了2秒.怎么了?具体来说,我有以下代码,其中该进程运行多个线程.有趣的是,有了这个长信号处理程序,其他线程似乎无法执行...谁能解释为什么会这样?

There is a timer which sends out signal SIGALARM every 1 sec. A signal handler which sleeps 2 sec is registered. What happens? Specifically, I have following code, in which the process runs multiple threads. It's quite interesting that with this long signal handler, it looks other threads are blocked from execution... Can anyone explain why this is the case?

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> //rand
#include <sys/wait.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#define NUM_THREADS 2

int init_timer(int real_time, int msec) {
    struct itimerval timeslice;
    timeslice.it_interval.tv_sec = msec / 1000;
    timeslice.it_interval.tv_usec = (msec % 1000) * 1000;
    timeslice.it_value.tv_sec = msec / 1000;
    timeslice.it_value.tv_usec = (msec % 1000) * 1000;
    setitimer(real_time ? ITIMER_REAL : ITIMER_VIRTUAL, &timeslice, NULL);
    return 0;
}

void install_handler(int signo, void(*handler)(int)) {
    sigset_t set;
    struct sigaction act;

    /* Setup the handler */
    act.sa_handler = handler;
    act.sa_flags = SA_RESTART;
    sigaction(signo, &act, 0);

    /* Unblock the signal */
    sigemptyset(&set);
    sigaddset(&set, signo);
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    return;
}

void timerTest(int signo)
{
    printf("000\n");
    sleep(1);
    printf("111\n");
}

void * threadTest(void * threadId)
{
    while(true)
    {
        printf("222\n");
    }
}

int main(int argc, char *argv[]) {
    int real_time = 1;
    int tick_msec = 10;
    init_timer(real_time, tick_msec);
    install_handler(real_time ? SIGALRM : SIGVTALRM, &timerTest);

    pthread_t threads[NUM_THREADS];
    int rc;
    long t;
    for (t = 0; t < NUM_THREADS; t++) {
        rc = pthread_create(&threads[t], NULL, threadTest, (void *) t);
        if (rc) {
            exit(-1);
        }
    }

    void * status;
    for (t = 0; t < NUM_THREADS; t++) {
        rc = pthread_join(threads[t], &status);
        if (rc) {
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

打印输出:

222
222
222
222
...
222
000
111
000
111
...

在第一个111出现之后将没有222吗?为什么会这样?

there will be no 222 after the first 111 occurs? why so?

推荐答案

信号传递到特定线程,因此信号处理程序在特定线程(信号传递到的线程)中运行.如果将信号传递给写出222\n的线程,则该线程必须停止写出222\n并运行信号处理程序.您的示例信号处理程序需要一整秒才能运行,因此这是一整秒,在此期间该线程可能不会写出222\n.

The signal is delivered to a particular thread, so the signal handler runs in a particular thread (the thread the signal was delivered to). If the signal is delivered to a thread writing out 222\n, then that thread must stop writing out 222\n and run the signal handler. Your example signal handler takes a full second to run, so that's a full second during which that thread may not write out 222\n.

此外,由于您正在使用printf来写出所有这些字节,因此libc中进行了一些锁定.由于printf不是异步信号安全"功能,因此实际上不确定,如果在信号处理程序中使用它会发生什么.您观察到的行为的一种可能解释是此.如果在该线程持有stdout锁的同时将信号传递给该线程,则在处理程序返回并且该线程中运行的正常"代码可以释放该锁之前,其他线程将无法写入stdout.但是,在这种情况下,信号处理程序仍可以写入stdout,因为锁是一个 rlock ,可以在任何特定线程中重复获取该锁.不过,这可能会有所不同,具体取决于特定的平台,C库,线程库或月相.不过,您的示例很容易转换为使用write(2),它可以演示或多或少相同的问题行为,或多或少具有相同的解决方案,并且不依赖于未定义的行为.

Additionally, since you are using printf to write out all of these bytes, there is some locking being done in libc. Since printf is not an "async signal safe" function, it's actually undefined what happens if you use it in a signal handler. One possible explanation for the behavior you observe is this. If the signal is delivered to a thread while that thread holds the stdout lock, then no other thread will be able to write to stdout until the handler returns and the lock can be released by the "normal" code running in that thread. The signal handler can still write to stdout in this case, though, because the lock is an rlock which can be acquired repeatedly any particular thread. This may vary from depending on the specific platform, C library, thread library, or phase of the moon, though. Your example is easily converted to use write(2) though, which demonstrates more or less the same problem behavior, has more or less the same fix, and doesn't rely on undefined behavior.

如果您在222\n线程中SIG_BLOCK计时器信号,那么信号处理程序将始终在主线程中运行,并且在信号处理程序休眠时您将继续获得222\n输出.

If you SIG_BLOCK the timer signal in the 222\n threads, then the signal handler will always run in the main thread and you will continue to get 222\n output while the signal handler sleeps.

Seth还非常重视仅在信号处理程序中使用安全功能.使用其他任何代码都意味着您的程序的行为是不确定的.

Seth also makes a great point about only using the safe functions in signal handlers. Using any others means your program's behavior is undefined.

这篇关于带有长信号处理程序(SIGALARM)的计时器的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆