2013-03-14 24 views
3

爲了創建高精度定時器,我編寫了一個模塊,該函數使用timer_create()函數實例化POSIX定時器。它使用CLOCK_REALTIME作爲其時鐘類型,SIGEV_SIGNAL作爲通知方法,SIGRTMIN作爲信號編號。它的信號處理器除了sem_post()之外什麼都不做。定時器使用timer_settime()啓動,其中任何毫秒數作爲定時器間隔。POSIX定時器以預期頻率的兩倍運行

該模塊的用戶可以等待計時器滴答;等待功能基本上由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) 
+2

你有沒有考慮在'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

+0

@BasileStarynkevitch:不,我沒有考慮過使用'timerfd_create',會考慮這一點。我也沒有做'strace'。遵循您的建議,我發佈了代碼並上傳了一個用於重現問題的軟件包(此處)(http://temp-share.com/show/HKdP6zUCA)。 – 2013-03-14 13:41:00

+0

@BasileStarynkevitch:'strace'揭示了問題的原因。請參閱下面的答案。謝謝你的幫助! – 2013-03-14 19:38:59

回答

2

根本原因這個問題的原因是sem_wait()被喚醒了兩次:一次是因爲它被信號中斷,o因爲信號量由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; 
} 

感謝巴西萊Starynkevitch的建議使用strace,它揭示了問題的原因。

相關問題