2014-11-13 80 views
1

我正在將我們的一個嵌入式微控制器庫移植到linux上,並在其周圍編寫一個python包裝器。SIGALRM,間隔定時器和睡眠問題()

我的低級模塊之一取決於每10ms調用一次回調。這個回調計數每個軟件定時器都有自己的回調。這些計時器在我們的圖書館中使用,儘管它們不必百分之百準確,但它們預計會持續滴答,並有望達到+/- 10%的準確度。該模塊模仿微控制器內的標準定時器中斷。

我有一個現有的實現使用setitimer和SIGALRM處理程序,它奇妙地工作。下面是我的一些代碼:

void halCounterInit(void) 
{ 
    struct sigaction alrm_action; 
    alrm_action.sa_handler = timerInterrupt; 
    alrm_action.sa_flags = SA_RESTART; 
    sigaction(SIGALRM, &alrm_action, NULL); 
} 

void halCounterEnable(void) 
{ 
    mytime.it_interval.tv_sec = 0; 
    mytime.it_interval.tv_usec = 10000; //10ms 
    mytime.it_value.tv_sec = 0; 
    mytime.it_value.tv_usec = 10000; //10ms 
    setitimer(ITIMER_REAL, &mytime, NULL); 
} 

void halCounterDisable(void) 
{ 
    mytime.it_interval.tv_sec = 0; 
    mytime.it_interval.tv_usec = 0; 
    mytime.it_value.tv_sec = 0; 
    mytime.it_value.tv_usec = 0; 
    setitimer(ITIMER_REAL, &mytime, NULL); 
} 

void halCounterRegisterInterruptHandler(InterruptHandlerCallback cb, void *params) 
{ 
    myInterruptHandler = cb; 
    myParams = params; 
} 

void timerInterrupt(int signum) 
{ 
    if (myInterruptHandler != NULL) 
    { 
     myInterruptHandler(myParams); 
    } 
} 

在測試一些該代碼與其他代碼一起的,我注意到調用睡覺()揪出來早產生SIGALRM信號時。我在python中使用ctypes與gevent(基於libevent)接口,以便在C庫上創建一個新圖層。我擔心這個SIGALRM問題會導致很難找到Python中的錯誤。

我試圖用一個pthread和一個阻塞select()調用來重寫我的簡單計數器,但看起來select()調用提前退出,我的計時器變得非常不準確。

  1. 有沒有辦法讓setitimer生成一個我可以捕獲的信號,而不是sigalrm?
  2. 有沒有另一種方法來做到這一點,而不使用信號?我已經嘗試了睡眠,在另一個線程中選擇和定期睡眠,但那不太好。

任何幫助表示讚賞。理想情況下,這對OSX和Linux都有效,但Linux是唯一的硬性要求。

編輯:使用select()或了nanosleep

我的單獨的線程代碼()。兩者都不準確。選擇的實現被註釋掉。 MyTime在init函數中正確配置。這是線程功能:

while (TRUE) 
{ 
    pthread_mutex_lock(&Lock); 
    //we are paused 
    while(!Running) 
    { 
     //we are waiting for a signal to tell us when to start again 
     pthread_cond_wait(&Cond, &Lock); 
    } 
    pthread_mutex_unlock(&Lock); 
    RemTime.tv_nsec = 0; 
    do 
    { 
     nanosleep(&MyTime, &RemTime); 
    } 
    while(RemTime.tv_nsec != 0); 
    //select(0, NULL, NULL, NULL, &MyTime); 
    if (myInterruptHandler != NULL) 
    { 
     myInterruptHandler(myParams); 
    } 
} 

謝謝。

+0

Re#1,是的,但這是棘手的要做的正確(tm)作爲@abligh建議下面,尤其是在_library_代碼(它不能斷言信號的自由控制)棘手。 Re#2,是的,但我們無法幫助您調試我們無法看到的代碼。 (順便說一句,SIGALRM或SIGVTALRM?你的敘述和你的代碼說了不同的東西。) – pilcrow

+0

@pilcrow,SIGALRM,我剛剛意識到我複製了一些我正在玩的調試代碼。將編輯。 –

回答

2

只要出現非屏蔽信號,它就會中斷任何系統調用運行。有一個SA_RESTART選項(有關詳細信息,請參閱man -s7 signal),但這隻會重新啓動某些系統調用(而不是例如sleep)。無論您使用何種信號,如果系統調用中斷,則受限於上述條款,行爲將會改變。因此,您需要檢查整個應用程序,確保正確處理返回碼EINTR。當然,如果您使用sleep並期望可靠性,那很難。

我建議你最好不要使用信號,特別是如果你正在使用線程;兩人打得不好。你說你希望它是可移植的:另一個避免鼠疫等信號的原因。

我會做的是設置另一個線程來執行您的定時器,並使用適當的互斥鎖來保護計時器值。該線程可以睡眠,直到需要運行下一個線程,或者使用nanosleeppthread_cond_timedwait(如果您想讓條件突破它)。無論是哪種情況,您都需要打包呼叫以避免導致錯誤退出的信號。在通話之前讀取時鐘的當前值,將呼叫包裝在do/while()循環中,然後測試時間是否真的超過了您在測試條件下所需的時間。傳統上,用戶使用gettimeofday進行此操作,但這並不總是單調的,因此您可能需要clock_gettimeCLOCK_MONOTONIC,具體取決於您對系統時間更改的反應方式。

+0

謝謝你的repsonse。我確實嘗試使用單獨的線程以及select()和nanosleep,但無法獲得正確的工作方式。我想我可以嘗試充分包裝代碼以解釋進入和中斷它的信號。我在EINTR上得到你的觀點,但它不一定是我擔心的代碼,而是我可能與之交互的任何各種python庫。 –