2012-07-26 74 views
1

當我在寫一個使用定時器以固定的採樣率(200Hz的)做一些數據採集和處理應用程序中處理「intterupted系統調用」的錯誤。 應用程序像服務器一樣運行並在後臺運行。它應該可以從其他進程或來自UDP的其他機器進行控制。使用定時器

要做到這一點,我用timer_create()API定期生成SIGUSR1和調用完成的採集和處理的處理程序。

配置定時器的代碼是如下(爲了清楚減去錯誤校驗):

sa.sa_flags = SA_SIGINFO; 
sa.sa_sigaction = handler; 
sigemptyset(&sa.sa_mask); 
sigaction(SIGUSR1, &sa, NULL); 
sev.sigev_notify = SIGEV_SIGNAL; 
sev.sigev_signo = SIGUSR1; 
sev.sigev_value.sival_ptr = &timerid; 
timer_create(CLOCK_REALTIME, &sev, &timerid); 
timer_settime(...) 

當從UDP接收到「啓動」命令上面的代碼被調用。爲了檢查命令,我在調用recvfrom()系統調用的主程序中有一個無限循環。

問題是,當收到'start'命令,然後定時器正常啓動並運行(使用上面的代碼)時,由於SIGUSR1,我得到'中斷系統調用'錯誤(EINTR)定時器發送的信號中斷了recvfrom()調用。如果我檢查這個特定的錯誤代碼並忽略它,當調用recvfrom()時,我終於得到'連接被拒絕'錯誤。

所以在這裏我的問題:

  1. 如何解決這個「中斷的系統調用」的錯誤,因爲它似乎 忽略它,重新做recvfrom的()不工作?
  2. 爲什麼在大約20次嘗試後出現「連接被拒絕」錯誤?
  3. 我有一種感覺,使用SIGEV_THREAD可能是一個解決方案,我的理解是,創建一個新的線程(如phread_create)不產生信號。我對嗎?
  4. 信號號碼在這裏很重要嗎?使用實時信號還有什麼優點?
  5. 是否有任何其他的方式做什麼,我打算做的:具有從UDP和實時週期任務後臺循環檢查命令?

而且這裏的獎金問題:

  • 它是安全的進行數據採集和處理的處理程序或我應該使用一個信號量機制來喚醒一個線程,這樣做?

解決方案: 作爲一個答案,並在評論中建議,使用SA_RESTART似乎解決的主要問題。

解決方案2: 通過SIGEV_SIGNAL使用SIGEV_THREAD也起作用。我讀過使用SIGEV_THREAD可能需要比SIGEV_SIGNAL更多的資源的地方。但是,我沒有看到有關任務時間的重大差異。

+0

我並不完全瞭解你的情況,但是避免在信號處理程序中做「真正的工作」的一種優雅方法是打開一個管道到自身,讓信號處理程序寫入管道,並讓main選擇循環(我猜你有一個)從管道讀取並做真正的工作。我不知道這是否能滿足您的時間需求。至少管道避免了併發問題,因爲從管道讀取和寫入是原子的(並且在它們之間存在上下文切換),這也避免了需要連續切換sigmask。 – wildplasser 2012-07-26 20:04:43

+0

我不確定我是否理解了你的所有問題,但是你可以嘗試將'SA_RESTART'添加到'sa_flags'嗎? – ninjalj 2012-07-26 20:12:41

+0

wildplasser>我沒有任何選擇循環,因爲我只有一件事要做:檢查通過帶有'recvfrom' – pahpaul 2012-07-26 22:38:52

回答

1

定時器傾向於使用SIGALARM來實現。

信號接收,包括SIGALARM,往往會導致長時間運行的系統調用在errno中以EINTR提前返回。

SA_RESTART是解決此問題的一種方法,因此係統調用會因接收到信號而中斷,並且會自動重新啓動。另一種方法是檢查系統調用errno的EINTR,並在收到EINTR時重新啓動它們。

使用read()和write()當然,你不能只是重新啓動,你需要選擇你離開的地方。這就是爲什麼這些會返回傳輸數據的長度。

+0

OP沒有使用警報(2),他使用timer_create,因此他處理SIGUSR1。 – Giel 2012-07-26 20:42:22

+0

正如Giel所說,我正在使用timer_create而不是報警。無論如何,我會嘗試使用SA_RESTART標誌。 – pahpaul 2012-07-26 22:27:55

1

既然你使用Linux,那麼我會選擇使用timerfd_create代替。

這樣,您可以只用select(2),poll(2)epoll(7)代替,並且在主循環中處理定時器事件時沒有信號處理程序的困難。

至於EINTR(中斷系統調用),只需重新啓動中斷的特定系統調用即可正確處理這些事件。

+0

不幸的是'timerfd_create'在我的平臺上不可用。 – pahpaul 2012-07-26 22:21:47

0

重新啓動中斷的系統調用是對EINTR的正確響應。你「連接被拒絕」的問題是一個不相關的錯誤 - 在一個UDP套接字上,它指示在該套接字上發送的先前數據包被目的地拒絕(通過ICMP消息通知)。

0

問題5:您使用消息和實時定期線程是完全正確的。不過,我建議你完全避免使用定時器,正是因爲它們使用了信號。我自己遇到了這個問題,並最終用一個簡單的clock_nanosleep()替換了計時器,該計時器使用TIMER_ABSTIME,並更新了時間以保持所需的速率(即將週期添加到絕對時間)。結果是代碼更簡單,沒有更多信號問題,以及比基於信號的定時器更準確的定時器。順便說一句,你應該在處理程序中測量你的計時器的週期,以確保它足夠準確。我對定時器的經驗是8年前,所以準確性問題可能會得到解決。然而,信號的其他問題是信號本身固有的,因此不能被「解決」 - 只能解決問題。

另外,我發現從處理程序獲取數據沒有問題,它肯定會減少檢索數據的延遲。