Linux, 工作, 生活, 家人

Linux, Programming

SIGALARM / timer_create 造成 CPU sys 100% 的問題

最近遇到一個怪問題, 某一隻程式跑起來的時候, 有一定的機率 sys 佔有率是 100%

Cpu0  :  0.0%us,100.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st

原來的 code 長的像是這樣

#include 

如果用 perf 下去看, 大概長得像這樣,  kernel space 的 wait 不是真正的 wait

   
66.86%  swapper  [kernel.kallsyms]  [k] r4k_wait
        |
        --- r4k_wait
        cpu_idle

32.21%    test  test               [.] main
0.12%     test  [kernel.kallsyms]  [k] finish_task_switch
        |
        --- finish_task_switch
        |
        |--54.54%-- __schedule
        |          schedule
        |          |
        |          |--83.34%-- work_resched
        |          |
        |           [--16.66%-- schedule_timeout
        |                     do_sigtimedwait
        |                     SyS_rt_sigtimedwait
        |                     handle_sys64
        |
        --45.46%-- schedule_tail

打開 kernel spin lock stat 也看不出所以然來(註: 上面是 100% sys cpu 的, 下面是正常的

spinlock

spinlock

所以這就讓人很困擾, 到底出了什麼問題
做過一些測試, 例如:
Enable Realtime option , 改用 tickless or 1000HZ tick , 檢查 spin lock deadlock ,
叫出 Sysrq 出來看(看不出來有什麼特別的)
不過仍然會發生, 百思不得其解.

除了自己的 Embedded System , 連 Ubuntu PC 也可以複製的出來,
所以這就排除了 SoC vendor 的問題, 理論上我們的應用不是特別奇怪.
所以心中訥悶, 這世界上這麼多人只有我們有問題嗎?

關於這個問題的人討論的很少, 所以可能要多翻一下 Google , 多濾一些關鍵字.

直到翻到這個網站, WHY YOU SHOULD AVOID USING SIGALRM FOR TIMER
他的說法是

The problem occurs when the signal function is triggered just before we call setitimer().
The signal handler blocks, as the mutex is currently being held by do_timer_bookkeeping() and this is the deadlock.

不過我們沒有 deadlock , 但是他的發想很好, 仍然可以參考他講的去做

To fix it, I removed all the SIGALRM code and replaced it with setitimer.
I set the previous SIGALRM handler as the thread that will be spawned whenever the timer expired.

看起來是好主義

所以我就用 timer_create 改寫了一下這隻程式,
好的事情是, 情況有改善, 不會在 10 分鐘就發生,
壞的事情是, 仍然會發生. 雖然極難複製這個問題, 但是會發生就是很討厭, 會留一個尾巴沒有辦法收尾.

所以持續在看有沒有其他的解決方案.
(timer_create 的寫法可以參考 Linux timer)

中間有翻到 pthread 的 sigmask 用 thread 處理 SIGALRM 訊號,
不過多想一下, 個人認為應該也會踏到同樣的問題, 所以做完功課之後, 這個方式晚一點再說.

又翻到這一篇setitimer, SIGALRM & multithread process (linux, c) 其中有說到,

In the topic, Andi Kleen (Intel) recommends to switch toPOSIX timers (timer_create)“; and in ML thread Davide Libenzi suggests use of timerfd (timerfd_create, timerfd_settime) on non-ancient Linuxes.

答案就呼之欲出了, 要改用 timerfd 改寫我目前的 timer_create.
不過 timerfd 是 trigger fd 用的, 不會呼叫 call back function, 所以要配合 epoll 用, 而且 epoll 是 IO event trigger , 所以要配合 pthread 去處理 epoll .

程式改寫完的結果, 看起來效能比 SIGALRM / timer_create 好. 但是複雜許多, 有參考別人的 sample code ,
不過考量這些 code 要處理 multiple fd, 而我目地只是取代原來的 SIGALRM, 所以又再更精簡化, 一般會更複雜的.

程式碼

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

其他有些資訊是提到 top 計算不準的問題

https://www.kernel.org/doc/Documentation/cpu-load.txt
https://lkml.org/lkml/2007/2/12/6

ref.

 

 

發佈留言