爲了創建高精度定時器,我編寫了一個模塊,該函數使用timer_create()
函數實例化POSIX定時器。它使用CLOCK_REALTIME
作爲其時鐘類型,SIGEV_SIGNAL
作爲通知方法,SIGRTMIN
作爲信號編號。它的信號處理器除了sem_post()
之外什麼都不做。定時器使用timer_settime()
啓動,其中任何毫秒數作爲定時器間隔。POSIX定時器以預期頻率的兩倍運行
該模塊的用戶可以等待計時器滴答;等待功能基本上由sem_wait()
實現。我的單線程測試應用程序創建了定時器並以期望的時間間隔i
毫秒啓動它。然後它循環,等待定時器觸發的x
次。它使用gettimeofday()
來計算所有這些。
預期循環的總時間爲x
* i
毫秒。相反,它只需要, 0.5 * x
* i
毫秒。我嘗試了幾種x
和i
的組合,測試的總執行時間從幾秒到幾十秒不等。結果始終如一,計時器以預期/期望頻率的兩倍運行。
這個運行在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/Linux
與gcc 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)
你有沒有考慮在'poll'中使用'timerfd_create' http://man7.org/linux/man-pages/man2/timerfd_create.2.html http://man7.org/linux/man-pages/man2 /poll.2.html?你真的應該顯示一個小代碼......你是否對你的應用程序進行了劃分? – 2013-03-14 06:15:57
@BasileStarynkevitch:不,我沒有考慮過使用'timerfd_create',會考慮這一點。我也沒有做'strace'。遵循您的建議,我發佈了代碼並上傳了一個用於重現問題的軟件包(此處)(http://temp-share.com/show/HKdP6zUCA)。 – 2013-03-14 13:41:00
@BasileStarynkevitch:'strace'揭示了問題的原因。請參閱下面的答案。謝謝你的幫助! – 2013-03-14 19:38:59