POSIX Timer: обработчик сигнала для таймера
Этот пост связан с: POSIX ТАЙМЕР - есть несколько таймеров
Я хочу вызвать функцию в 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 ответ
Спецификация POSIX точно указывает, какие функции API вы можете безопасно вызывать из обработчика сигнала (список находится в конце раздела 2.4.3).
socket
, connect
, recv
, а также close
все в списке, поэтому они должны быть в безопасности. printf
а также fprintf
это не так, что неудивительно, поскольку они управляют буфером пространства пользователя, который трудно поддерживать согласованным при наличии асинхронных сигналов.
Так что кроме printf
Тип звонков, этот код на самом деле выглядит нормально для меня. Когда ваш "код зависает", вы пытались подключиться с помощью отладчика, чтобы увидеть, где вы застряли?
Тем не менее, обычный способ справиться с этим является одним из двух способов.
Первый способ: если в вашем приложении есть "цикл обработки событий", подготовьте сигнал просто для отправки события. Тогда ваш основной поток обрабатывает это событие, как и любое другое, и все остается приятным и синхронным.
Второй способ: посвятить поток периодической задаче. Поток может просто зациклить вызов sleep(1) и выполнение вашей периодической задачи.
Но я все еще думаю, что ваш подход здесь должен работать.