2013年2月11日 星期一

async 與 non-blocking IO

以前一直沒有分清楚 asynchronous 和 non-blocking 的差異, 寫久了以後才明白兩者是不同層次的東西。

  • asynchronous programming: 程式不是一條線走到底, 可能目前做到一個段落停下來, 「過一會兒」再繼續執行
  • non-blocking IO: 呼叫讀寫函式時, 不會等到完成才返回

通常要讀寫外部資料時 (一般檔案、網路、pipe 等) , 無法確定返回的時間, 這樣會阻擋程式的運作, 所以希望這個讀取 (或寫入) 不要等完成後才返回 (即「非同步」)。

若不希望此次呼叫有不確定時間的擔擱, 有兩種可能的作法:

  • 開新 thread 或用既有的 thread 進行讀寫。總之, 停是停別人家的 thread, 和本 thread 無關
  • 設定 file descriptor 為 O_NONBLOCK, 告訴 OS 這個 fd 為 non-blocking IO, 呼叫後要能立即返回

兩者作法的優缺點分析已超出本「隨手記」的範圍 (書本作者愛用的大絕), 需要特別注意的是, 設了 O_NONBLOCK 不表示呼叫 read/write 後就結束了。

man 2 read (或 man 2 write) 的 ERRORS 提到:

  • EAGAIN The file descriptor fd refers to a file other than a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.
  • EAGAIN or EWOULDBLOCK The file descriptor fd refers to a socket and has been marked nonblocking (O_NONBLOCK), and the read would block. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.

意思是說:

  • 招對實體檔案無效 (為了檔案讀寫效能, 一次做完可減少硬碟更動讀寫頭的時間)
  • 對 socket 仍有可能失效, 記得用 select / epoll 之類的 system call 請系統在有機會完成 non-blocking 操作時通知你。也就是說, 系統只保證此次呼叫不會 block 你, 不保證此次呼叫一定會成功。由開發者持續嘗試呼叫, 總有完成的時候。

2 則留言:

  1. 您好,如果是平行運算(資源充足)的話,就不太會有"停下來,過一會而再執行"的問題
    換句話說,如果今天只執行一個程式,其中它有個sleep(1),我們總不能稱它為非同步吧
    以我的理解,同步跟非同步就是"等"跟"不等"的問題
    您也寫到"不要等完成後才返回 (即「非同步」)"
    所以我們的理解應該是一致的?
    另外我想"非同步"只是個廣易的統稱
    而non-blocking只是其中一種實作方式
    另外像是callback也是一種實作方式

    回覆刪除
    回覆
    1. 這錯大了,blocking, non-blocking和sync async是兩群不同的概念
      blocking non-blocking是指物理意義上的等或不等
      舉例來說non-blocking IO是指這一次IO從頭到尾保證constant time回傳
      (但他可能只回傳狀態,所以你需要做polling)

      sync async是指呼叫API當下等或不等
      async不代表你真的不用等,你只是召喚一個替身幫你等或者你現在偷懶不做,事後再做而已
      如果你async的去呼叫一個block API
      實際上你還是要等的,而且他仍然不保證常數時間內回傳
      所以你會發現舉凡async的API通常都有個flush或wait就是在做幫你等完的動作

      刪除

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...