【问题标题】:POSIX Timer : Signal Handler for Timer freezesPOSIX 计时器:计时器冻结的信号处理程序
【发布时间】:2011-06-22 13:31:44
【问题描述】:

这篇文章与: 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;
}

【问题讨论】:

  • 您确定这是最好的方法吗?为什么不使用gettimeofday() 测量经过时间的线程(或您的主线程)然后启动任务?这不会精确到毫秒,但在大多数 Linux 中会在 +- 10 毫秒内。
  • 我添加了一个 pthread 条件变量,它在计时器到期时设置。现在 getspeed() (更改为线程)等待此变量进行套接字通信。它工作正常,但我想听听你对此的所有意见

标签: c timer posix signals


【解决方案1】:

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

socket、connect、recv 和 close 都在列表中,因此它们应该是安全的。 printf 和 fprintf 不是,这并不奇怪,因为它们管理用户空间缓冲区,在存在异步信号的情况下很难保持一致。

所以除了printf 类型调用之外,这段代码对我来说实际上看起来不错。当您的“代码冻结”时,您是否尝试过附加调试器来查看卡在哪里?

也就是说,通常的处理方法是两种方法之一。

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

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

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

【讨论】:

  • 谢谢 Nemo :) 我现在尝试的第一件事是注释掉 printf 并测试了大约 12-13 次,直到现在这种冻结没有弹出。 (作为第二个措施,我还尝试将两个信号处理程序例程分开 SIG 和 SIGTIMER 并在此处注释 printf )。我喜欢周期性任务的线程想法。但不想在里面睡觉。我可以使用单独的线程以任何其他方式实现它吗?就像为那个线程调用一个计时器之类的?
  • 为什么不想在线程中调用 sleep()?如果您担心 sleep() 本身可能会设置一个计时器,您可以使用 select() (在零描述符上)超时。这应该非常有效; select() 将阻塞,内核将使线程进入睡眠状态,它将消耗零资源,直到内核决定将其唤醒...
  • 哦,好的。我会试试的。实际上,现在我的代码再次冻结(删除 printfs 后:(...)我该怎么办?
  • 用gdb附加并查看回溯。
  • select 需要三组文件描述符和一个超时。如果集合为空,您可以单独使用超时来执行高分辨率“睡眠”。或者,如果您只关心现代 POSIX 系统,只需使用nanosleep,它保证“对任何信号的动作或阻塞都没有影响”。
猜你喜欢
  • 2012-02-18
  • 1970-01-01
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 2020-11-08
相关资源
最近更新 更多