POSIX Timer: обработчик сигнала для таймера - PullRequest
1 голос
/ 22 июня 2011

Это сообщение относится к: POSIX TIMER - есть несколько таймеров

Я хочу вызвать функцию в 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 Ответ

2 голосов
/ 22 июня 2011

Спецификация POSIX точно сообщает, какие функции API можно безопасно вызывать из обработчика сигнала (список находится в конце раздела 2.4.3).

socket, connect, recv и close находятся в списке, поэтому они должны быть в безопасности.printf и fprintf - нет, что неудивительно, поскольку они управляют буфером пользовательского пространства, который трудно поддерживать согласованным при наличии асинхронных сигналов.

Таким образом, кроме типа printfзвонки, этот код на самом деле выглядит нормально для меня.Когда ваш «код зависает», вы пытались подключиться с помощью отладчика, чтобы увидеть, где вы застряли?

Тем не менее, обычный способ справиться с этим - один из двух способов.

Первый способ: Если в вашем приложении есть «цикл обработки событий», подготовьте сигнал просто для отправки события.Тогда ваш основной поток обрабатывает это событие, как и любое другое, и все остается приятным и синхронным.

Второй способ: посвятить поток периодической задаче.Поток может просто зациклить вызов sleep (1) и выполнение вашей периодической задачи.

Но я все же думаю, что ваш подход здесь должен работать.

...