POSIX定时器:定时器的信号处理程序冻结

时间:2011-06-22 13:31:45

标签: c timer posix signals

这篇文章与以下内容有关: POSIX TIMER- Have multiple timers

我想在SignalHandler中调用一个函数。此函数是TCP套接字客户端(getSpeed)。每次计时器滴答一秒钟时它从服务器获取数据并发送一个信号,然后调用相应的处理程序。

首先,我不确定从信号处理程序调用函数是否是一个好习惯。

现在的问题是,每当我执行与服务器通信的程序时,我的代码会随机冻结。

如果这种使用信号定义定时器的方式不好(并且可能导致一些隐藏的竞争条件)是否有更好的方法来编码上述场景?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <linux/socket.h>
#include <time.h>


static timer_t     tid;
static timer_t     tid2;

void SignalHandler(int, siginfo_t*, void* );
timer_t SetTimer(int, int, int);

int getSpeed(void) {

    int sockFD = 0;
    struct sockaddr_in addr;
    int numbytes;
    unsigned char rxMsg[128];

    /* open socket */
    if((sockFD = socket(AF_INET, SOCK_STREAM, 0)) <= 0) {
        fprintf(stderr, "%s: error while creating socket\n", __func__);
        return -1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    inet_aton(IP, (struct in_addr *)(&(addr.sin_addr)));
    addr.sin_port = htons(DATA_PORT);
    if (connect(sockFD,(struct sockaddr *) &addr,sizeof(addr)) < 0)
        fprintf(stderr, "%s: failed to connect to server\n", __func__);

    if((numbytes = recv(sockFD, rxMsg, sizeof(rxMsg), 0)) < 1){
        fprintf(stderr, "%s: failed to recv data from vehicle\n", __func__);
        close(sockFD);
        return -1;
    }
    printf("bytes received from Client is : %d\n", numbytes);

    printf("Data  = %s\n",rxMsg);

    // close socket
    close(sockFD);
    return 0;
}
int main(int argc, char *argv[]) {


    struct sigaction sigact;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = SA_SIGINFO;
    sigact.sa_sigaction = SignalHandler;
    // set up sigaction to catch signal
    if (sigaction(SIGTIMER, &sigact, NULL) == -1) {
        perror("sigaction failed");
        exit( EXIT_FAILURE );
    }

    // Establish a handler to catch CTRL+c and use it for exiting.
    sigaction(SIGINT, &sigact, NULL);
    tid=SetTimer(SIGTIMER, 1000, 1);

    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = SignalHandler;
    // set up sigaction to catch signal
    if (sigaction(SIG, &sa, NULL) == -1) {
        perror("sa failed");
        exit( EXIT_FAILURE );
    }

    // Establish a handler to catch CTRL+c and use it for exiting.
    sigaction(SIGINT, &sa, NULL);
    tid2=SetTimer(SIG, 1000, 1);
    for(;;);
    return 0;
}

void SignalHandler(int signo, siginfo_t* info, void* context)
{
    if (signo == SIGTIMER) {
        //printf("Command Caller has ticked\n");

    }else if (signo == SIG) {
        //printf("Data Caller has ticked\n");
        getData();

    } else if (signo == SIGINT) {
        timer_delete(tid);
        timer_delete(tid2);
        perror("Crtl+c cached!");
        exit(1);  // exit if CRTL/C is issued
    }
}
timer_t SetTimer(int signo, int sec, int mode)
{
    static struct sigevent sigev;
    static timer_t tid;
    static struct itimerspec itval;
    static struct itimerspec oitval;

    // Create the POSIX timer to generate signo
    sigev.sigev_notify = SIGEV_SIGNAL;
    sigev.sigev_signo = signo;
    sigev.sigev_value.sival_ptr = &tid;

    if (timer_create(CLOCK_REALTIME, &sigev, &tid) == 0) {
        itval.it_value.tv_sec = sec / 1000;
        itval.it_value.tv_nsec = (long)(sec % 1000) * (1000000L);

        if (mode == 1) {
            itval.it_interval.tv_sec = itval.it_value.tv_sec;
            itval.it_interval.tv_nsec = itval.it_value.tv_nsec;
        }
        else {
            itval.it_interval.tv_sec = 0;
            itval.it_interval.tv_nsec = 0;
        }

        if (timer_settime(tid, 0, &itval, &oitval) != 0) {
            perror("time_settime error!");
        }
    }
    else {
        perror("timer_create error!");
        return NULL;
    }
    return tid;
}

1 个答案:

答案 0 :(得分:2)

POSIX spec确切地告诉您可以从信号处理程序安全调用的API函数(在2.4.3节末尾附近有一个列表)。

socketconnectrecvclose都在列表中,因此它们应该是安全的。 printffprintf不是,这并不奇怪,因为他们管理用户空间缓冲区,在存在异步信号时很难保持一致。

除了printf类型调用之外,这段代码实际上对我来说还不错。当你的“代码冻结”时,你是否尝试使用调试器来查看你被困的位置?

那就是说,处理这种情况的常用方法有两种。

第一种方式:如果您的应用程序有“事件循环”,请安排信号只是为了发送一个事件。然后你的主线程处理该事件就像任何其他事件一样,并且一切都保持良好和同步。

第二种方式:将线程用于周期性任务。线程可以循环调用sleep(1)并执行定期任务。

但我仍然认为你的方法应该有效。