0%

IO Models and Desgin Pattern

對最近的心得做個總結。
這篇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
2
int len = recv(s, .....);
// Do other things

在這種Model之下,為了要解決Blocking的問題,就得使用Multi-Porcess或是Multi-Thread的方式來做。

Synchronous non-blocking I/O

將Device設定成Non-Blocking模式時,不管Operation成功與否都會立即回傳。當回傳值為EWOULDBLOCKEAGAIN表示資料未準備好,這時就能把注意力放在其他事情上了。雖然不會造成Blocking,不過確造成了嚴重的Busy waiting。

1
2
3
4
5
6
7
8
while (true)
{
int len = recv(s, .....);
if (len == EAGAIN)
{
// Do other things
}
}

如果有兩個以上的I/O operation要考慮,程式就變成

1
2
3
4
5
6
7
8
9
10
11
12
while (true)
{
int len1 = recv(s1, .....);
if (len1 == EAGAIN)
{
int len2 = recv(s2, ......)
if (len2 == EAGAIN)
{
// Do other things
}
}
}

這樣寫實在很冗長,且維護很麻煩,因此有了第三種Model的出現。

Asynchronous blocking I/O

透過OS提供的Polling Mechanism(poll/select/epoll/kqueue)之類的,來判斷是否友I/O Event發生,不過由於Application要等待Polling Mechanism的事件完成,因此屬於Blocing Model。比起上面方式的優點,就是他可以同時間聽多個Device的事件,避免無意義的Busy Waiting。

1
2
3
4
5
6
7
8
select(fdmax+1, &read_fds, NULL, NULL, NULL);
for(i = 0; i <= fdmax; i++)
{
if(FD_ISSET(i, &read_fds))
{
int len = recv(i, .....);
}
}

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。