gpt4 book ai didi

c - POSIX 计时器以预期频率的两倍运行

转载 作者:太空狗 更新时间:2023-10-29 11:15:17 25 4
gpt4 key购买 nike

为了创建高精度计时器,我编写了一个模块,使用 timer_create() 实例化 POSIX 计时器。功能。它使用 CLOCK_REALTIME 作为时钟类型,使用 SIGEV_SIGNAL 作为通知方法,使用 SIGRTMIN 作为信号编号。它的信号处理程序只做一个 sem_post()。 .使用 timer_settime() 启动计时器, 以任意毫秒数作为计时器间隔。

模块的用户可以等待计时器滴答;等待功能基本上由 sem_wait() 实现.我的单线程测试应用程序创建计时器并以所需的 i 毫秒间隔启动它。然后它循环,等待 x 次以触发计时器。它使用 gettimeofday()为这一切计时。

期望循环的总时间为 x*i 毫秒。相反,它只需要 正好 0.5*x*i 毫秒。我尝试了几种xi的组合,测试的总执行时间从几秒到几十秒不等。结果始终是计时器以两倍于预期/期望的频率运行。

这运行在 CentOS 5.5 Linux 2.6.18-194.el5 #1 SMP Fri Apr 2 14:58:14 EDT 2010 x86_64 x86_64 x86_64 GNU/Linuxgcc 4.1.2


我已经上传a stripped down version of the code其中包括编译代码的脚本和重现问题的测试。

定时器类本身的代码如下:

/* PosixTimer: simple class for high-accuracy timer functionality */

/* Interface */
#include "PosixTimer.h"

/* Implementation */

#include <pthread.h>
#include <time.h>
#include <signal.h>
#include <semaphore.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define TIMER_SIGNAL SIGRTMIN
#define ALLOCATE_AND_CLEAR(pVar) \
pVar = malloc(sizeof(*pVar)); \
memset(pVar, 0, sizeof(*pVar))
#define FREE_AND_NULL(pVar) \
free(pVar); \
pVar = NULL

struct PosixTimerImpl {
timer_t timerId;
struct itimerspec timeOut;
sem_t semaphore;
};


static void
PosixTimer_sigHandler(
int sig,
siginfo_t *info,
void *ptr)
{
PosixTimer *self = (PosixTimer *)(info->si_value.sival_ptr);

if (NULL != self) {
sem_post(&self->semaphore);
}
}

static void
PosixTimer_setTimeoutValue(
PosixTimer *self,
unsigned int msecInterval)
{
if (NULL != self) {
self->timeOut.it_value.tv_sec = msecInterval / 1000;
self->timeOut.it_value.tv_nsec = (msecInterval % 1000) * 1000000;
self->timeOut.it_interval.tv_sec = msecInterval / 1000;
self->timeOut.it_interval.tv_nsec = (msecInterval % 1000) * 1000000;
}
}

/* Public methods */

/**
* Constructor for the PosixTimer class. Ticks happen every <interval> and are not queued
*/
PosixTimer *
PosixTimer_new(
unsigned int msecInterval)
{
PosixTimer *self = NULL;

int clockId = CLOCK_REALTIME;
struct sigevent evp;
int status;

/* Construction */

ALLOCATE_AND_CLEAR(self);

/* Initialization */

PosixTimer_setTimeoutValue(self, msecInterval);

evp.sigev_signo = TIMER_SIGNAL;
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_value.sival_ptr = self;
status = timer_create(clockId, &evp, &self->timerId);
if (0 == status) {
sem_init(&self->semaphore, 0, 0);
} else {
printf("Error creating timer, retVal = %d\n", status);
FREE_AND_NULL(self);
}
return self;
}


/**
* Destructor
*/
void
PosixTimer_delete(
PosixTimer *self)
{
int status;

sem_post(&self->semaphore);
status = sem_destroy(&self->semaphore);
if (0 != status) {
printf("sem_destroy failed\n");
}
status = timer_delete(self->timerId);
if (0 != status) {
printf("timer_delete failed\n");
}
FREE_AND_NULL(self);
}


/**
* Kick off timer
*/
void
PosixTimer_start(
PosixTimer *self)
{
#define FLAG_RELATIVE 0
int status;
struct sigaction sa;

sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, TIMER_SIGNAL);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = PosixTimer_sigHandler;
status = sigaction(TIMER_SIGNAL, &sa, NULL);
if (0 != status) {
printf("sigaction failed\n");
} else {
status = timer_settime(self->timerId, FLAG_RELATIVE,
&self->timeOut, NULL);
if (0 != status) {
printf("timer_settime failed\n");
}
}
}


/**
* Wait for next timer tick
*/
void
PosixTimer_wait(
PosixTimer *self)
{
/* Just wait for the semaphore */
sem_wait(&self->semaphore);
}

用于显示问题的测试:

/* Simple test app to test PosixTimer */

#include "PosixTimer.h"
#include <sys/time.h>
#include <stdio.h>

int main(
int argc,
const char ** argv)
{

#define USEC_PER_MSEC (1000)
#define NSEC_PER_MSEC (1000000)
#define MSEC_PER_SEC (1000)

PosixTimer *timer1 = NULL;
struct timeval before, after;
double dElapsedMsecs;
int elapsedMsecs;
int iCount1;

printf("Running PosixTimer tests\n");

#define DURATION_MSEC (10000)
#define INTERVAL_MSEC_TEST1 (5)
#define ACCURACY_MSEC_TEST1 (100)


timer1 = PosixTimer_new(INTERVAL_MSEC_TEST1);
iCount1 = DURATION_MSEC/INTERVAL_MSEC_TEST1;
printf("Running test: %d milliseconds in %d cycles\n", DURATION_MSEC, iCount1);

gettimeofday(&before, NULL);
PosixTimer_start(timer1);
while (0 < iCount1) {
PosixTimer_wait(timer1);
//printf(".");
iCount1--;
}
gettimeofday(&after, NULL);
//printf("\n");

dElapsedMsecs = (after.tv_sec - before.tv_sec) * MSEC_PER_SEC;
dElapsedMsecs += (after.tv_usec - before.tv_usec) / USEC_PER_MSEC;
elapsedMsecs = dElapsedMsecs+0.5;

if ((ACCURACY_MSEC_TEST1 > (elapsedMsecs - DURATION_MSEC)) &&
(ACCURACY_MSEC_TEST1 > (DURATION_MSEC - elapsedMsecs))) {
printf("success");
} else {
printf("failure");
}
printf(" (expected result in range (%d -- %d), got %d)\n",
DURATION_MSEC - ACCURACY_MSEC_TEST1,
DURATION_MSEC + ACCURACY_MSEC_TEST1,
elapsedMsecs);

return 0;
}

结果是

-bash-3.2$ ./DesignBasedTest 
Running PosixTimer tests
Running test: 10000 milliseconds in 2000 cycles
failure (expected result in range (9900 -- 10100), got 5000)

最佳答案

这个问题的根本原因是sem_wait()被唤醒了两次:一次是因为被信号中断了,一次是因为信号量被释放真的需要唤醒通过 sem_post()。检查 sem_wait()errno = EINTR 的返回值解决了问题:

/**
* Wait for next timer tick
*/
int
PosixTimer_wait(
PosixTimer *self)
{
int result;

/* Just wait for the semaphore */
do {
result = (0 == sem_wait(&self->semaphore));
if (!result) {
result = errno;
}
} while (EINTR == result);
return result;
}

感谢 Basile Starynkevitch 建议使用 strace,这揭示了问题的原因。

关于c - POSIX 计时器以预期频率的两倍运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15399802/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com