2012年5月26日 星期六

atexit() 和 exit() 的注意事項

有時候程式不正常結束時, 希望透過 atexit() 最做低限度的善後程續。但試用的結果是, atexit() 本身滿好用的, 正常結束後會符合預期做事。但是呼叫 exit() 隱藏的問題, 卻不那麼直覺。

目前遇過兩個狀況:

  • 在 signal handler 內使用 exit(): 想在收到異常通知時透過 exit() 善後。結果可能會讓程式卡住掛不掉。這也許和註冊的 callback 做什麼事有關。不過 exit() 不是 async-signal-safe functions (見 man 7 signal), 文件也提到在 signal 內用非 async-signal-safe functions 的行為是未定義的, 所以這種狀況出包也沒什麼好探究的了。
  • multi-thread 的處理: 註冊的 callback 裡有和 thread 相關的善後機制, 然後在錯的 thread 裡或錯的時機呼叫 exit() 造成 dead-lock。當狀況異常想呼叫 exit() 時, 其實也沒太多心力將 thread 之間的行為弄得更好。

另外關於 _exit(), man page 提到它不會呼叫 callback, 但有一些 implementation dependent 的行為, 感覺上不怎麼踏實。可以確定的是, _exit() 會關掉 file descriptors 而可能造成未知的延遲 (文件又提到可用 tcflush 避免這點, 但看不懂到底是有效還是 implementation dependent)。所以若要確保程式會立即結束, 用 abort() 或 kill(getpid(), SIGKILL) 應該比較穩吧, 只是就無法提供 exit status 了。

Android 的 audio delay

遇到這問題後上網查了一下, 結果發現哀鴻遍野, 許多人提到 audio 從播放到真的出來, 有 500ms 的延遲。我以為聲音播放下去應該要立即出來的, 沒想到有這麼嚴重的延遲。

目前沒有對策, 只好從一堆討論中, 挑了幾個比較有代表性的連結備忘:

官方 ticket

其它來源

這兩篇是 2011 年底寫的, 提到最佳 Android 裝置也有 100ms 的延遲, 並且有附聲音和影片展示 audio 延遲是怎麼一回事 (*1)。

這個站提供測試軟體和各裝置測出來的數據, 這裡有人提到運作的原理是播出聲音後用內建麥克風錄回聲音, 藉此算出 audio delay。以這麼高的延遲時間來說, 應該可忽略計時中間的一些 overhead。我用 Galaxy Nexus 測出來的數據會在 200ms ~ 500ms 之間跳來跳去。

備註

*1 可怕的是, 我習慣這些延遲行為後, 竟然覺得第二個影片的展示感覺還好。這或許也可解釋開發者感受到的問題嚴重程度, 有時不如使用者高, 因為開發者已習慣這樣的表現, 或潛意識覺得這很難處理, 因此反應比較遲頓。

2012-05-26 更新

ScottG+ 上回了不少相關知識, 貼到這裡備忘:

(後退一步,講些對你解問題無立即幫助的觀察)

1. 在簡單的 interrupt driven PCM samples 播放運作中,系統 wakeup 次數與 audio delay 成反比。故增加 audio latency 可省電。

2. 漂亮的解法是應用可以隨需要將系統調成 interrupt 與 polling mode,且 polling 時可調整 timer interval 的觀念,寫一個『latency 需求低時省電,有 app 需要 low latency 時才常 wakeup 以達成 low latency』的系統,如:http://0pointer.de/blog/projects/pulse-glitch-free.html

3. 這是 Android 將 Linux userspace 全部砍掉重練的後遺症,desktop Linux 反而無『latency 與 power consumption traodeoff』問題:http://arunraghavan.net/2012/01/pulseaudio-vs-audioflinger-fight/

Apple 在 OSX 原本就有 low latency / soft realtime 的 audio API 所以 iOS 也比較了解、有顧到這邊的需求。

4. 各個 Android 裝置 audio delay 會不同是因為 audio codec 晶片廠商與手機系統廠有在測『播音樂、電影時系統電池可撐多久』。且 Android 靠近 audio device driver 這部份的架構讓他們可以微調,但這些廠商隨手也寫不出 timer based audio scheduling,軟體架構上鼓勵他們改的也只是『專屬於某硬體』的部份。

2013-07-13

情況總算有些進展, 見 Google I/O 2013 - High Performance Audio 了解細節。

iPhone 網路緩慢的原因

最近測一些網路連線的東西, 發覺 iPhone 4S 的行為硬是和各版的 iPad 不同, 照理說 iPhone 4S 比 iPad 新這麼多, 應該表現要更好才對。最後懷疑是無線晶片的問題。查了一下, 發覺不少人抱怨 iPhone 的網路問題, 甚至有人的情況是 Wi-Fi 的表現比 3G 糟。

去除 iOS 裝置的無線晶片良率問題 (*1), 原因大致上有兩種

  • 網路晶片的參數和無線 AP 有衝突, 修改 AP 參數後可改善
  • 網路晶片有省電功能, 會視負載率調整效能

第一點我沒打算嘗試, 從 app 的開發者來說, 就算我的 iPhone 因此變快但使用者的不會自動受惠, 也沒什麼意義。針對第二點做了些實驗, 發覺 MacBookAir、iPad 各型和 iPhone 4 和 4S 都有省電功能, 只是省電的作法有差, iPhone 的效能相對地不穩。

我的測試方法是

  • 下載 Ping 之類的 free app (我用 Network Ping Lite), 用 iOS 裝置 ping 區網或外面的機器, 比較 round-trip time。結果 iPhone 4S 可到 >400ms, 但桌機 <200ms。
  • 由 Network Ping Lite 得知 iOS 裝置的 IP, 用桌機 ping 在同一個區網下的 iOS 裝置。比較閒置時的 round-trip time 和使用 YouTube app 時的情況。結果在閒置時也是有較高的 rtt, 到幾百 ms 都有。但大量使用網路時, 表現符合預期, rtt <5ms, 只是偶而會跳出一個上百 ms。

從 OS 角度來看, 網路用得愈兇網路會愈順, 可讓電力用在刀口上。不過從開發者的角度來看, 這表示若希望一直保有 low latency, 要盡可能持續用網路; 或著, app 必須設計成可容許偶而的 low latency。這兩個選項還挺極端的, 不知系統有沒有可能提供選項可建議放寬網路省電機制。

參考資料

PS

* 我強烈懷疑 iOS 的無線晶片良率有問題。我第一個借來玩的 iPad, 它的 Wi-Fi 很不穩, 於是就沒什麼用它。再沒多久, 它變成根本連不上了, 但樣情況下用 G1 沒有問題。後來又試了別台 iPad 和 iPad 2, 一樣是 AP 放客廳, 我在房間裡使用, 結果 iPad 符合預期可以用, 但 iPad 2 在客廳轉角要進房間前就收不到訊號了。

2012年5月2日 星期三

SIGCHLD 和 zombie

關於 zombie

  • zombie: ps 列的 status 為 Z, 表示 process 已結束, 資源也釋放了, 等待 parent process 呼叫 wait/waitpid 回收它的 exit status, 裡面包含 exit code
  • zombie 是無法被 kill 的, 僵屍無敵啊!!
  • zombie 會占據極小的系統資源, 如可 forked 的 process 數
  • 不註冊 SIGCHLD = 註冊 signal handler 為 SIG_IGR => OS 會在 child process 掛掉時直接回收, 不會變 zombie

關於 SIGCHLD 收發時機

  • 即使 child process 收到 SIGKILL, 它也不會當下就掛掉而讓 parent process 在當下收到 SIGCHLD。process 得等取得 CPU 時才會「處理」 SIGKILL 而掛掉。總之, 系統不保證 SIGCHLD 收到的時間
  • child process 正式掛掉後, 當 parent process 取得 CPU 時, 系統會盡快讓它收到 SIGCHLD。換句話說, 若 parent process 被停住的期間「收到」 SIGCHILD, resume 後會立即收到 SIGCHILD
  • SIGCHLD 如同其它 signal, 不會被 queued, 只有保證在有註冊 handler 的情況下, 至少會呼叫一次 handler (細節說明請查: signal mask/block )
  • 註冊 handler 前收到 SIGCHLD 的話, 不同系統作法不同, 可能會補送也可能不會補送

關於 handler

  • signal handler 會被繼承 (見 man fork 開頭), 所以若 A 有註冊 SIGCHLD 則 A 的 child process 和後續 grandchild process 都有 SIGCHLD handler。寫 handler 時要注意是否能沿用下去, 或是明確在每次 fork 後重設一次 child process 的 signal handler
  • 標準寫法如下, status 裡會記錄許多有用資訊, 見 man waitpid:
int status;
while ((pid=waitpid(-1, &status, WNOHANG)) > 0) {
    // Do something
}

C++ 能否用 memcpy 複製 class / struct 的資料?

答案是: POD (plain old data) type 可以。POD type 可和 C 互通, CPP Reference POD Type 的介紹: Specifies that the type is POD (Plain Old Data) type. Thi...