對最近的心得做個總結。
這篇Boost application performance using asynchronous I/O描述了幾種常用的IO Model。
首先要先解釋兩個很像,卻又部太相同的名詞
- Synchronous Application發起I/O Operation,並且等待其完成 (如 read / write)
- Non-Synchronous Application僅發起I/O Program Request,由Kernel通知Application完成
- Blocking Application因為等待某事件,而不能繼續往下執行
- Non-Blocking Application不受事件影響
Synchronous blocking I/O
最常見的I/O Model,就是在Event未結束前不會將控制權交還給Application。
1 | int len = recv(s, .....); |
在這種Model之下,為了要解決Blocking的問題,就得使用Multi-Porcess或是Multi-Thread的方式來做。
Synchronous non-blocking I/O
將Device設定成Non-Blocking模式時,不管Operation成功與否都會立即回傳。當回傳值為EWOULDBLOCK
或EAGAIN
表示資料未準備好,這時就能把注意力放在其他事情上了。雖然不會造成Blocking,不過確造成了嚴重的Busy waiting。
1 | while (true) |
如果有兩個以上的I/O operation要考慮,程式就變成
1 | while (true) |
這樣寫實在很冗長,且維護很麻煩,因此有了第三種Model的出現。
Asynchronous blocking I/O
透過OS提供的Polling Mechanism(poll/select/epoll/kqueue)之類的,來判斷是否友I/O Event發生,不過由於Application要等待Polling Mechanism的事件完成,因此屬於Blocing Model。比起上面方式的優點,就是他可以同時間聽多個Device的事件,避免無意義的Busy Waiting。
1 | select(fdmax+1, &read_fds, NULL, NULL, NULL); |
Interrupt-driven socket I/O
這種I/O Model只存在於Unix系統,比起使用Select等的Polling Mechanism,使用Interrupt來通知Application事件完成。有趣幸的話可以參考Interrupt driven socket I/O。
Asynchronous non-blocking I/O
Application僅發送一次I/O Operation,之後就交給Kernel來處理,完成之後會發送一個信號給Applcation,通知I/O Operation的情形。之後的處理就交給Application來決定。在Windows底下有I/O Completion Ports,而Unix下有AIO。
遮編得範例比較複雜,可以參考這個Gist的寫法。
在編譯的時候記得加上-lrt
1 | $ gcc 01-aio_read_test.c -o 01-aio_read_test -lrt |
I/O Design Pattern
跟I/O 有關的Design Pattern分成兩大類
- Reactor
- Proactor
在這篇Comparing Two High-Performance I/O Design Patterns有仔細說明。 主要差異在事件處理邏輯的不同。
在Windows下沒有好得Reactor Library,IOCP幾乎等於Proactor,於是為了跨平台要求,Boost ASIO用的就是Proactor Model,其他Libev/Libevent等就是使用Reactor Model。