Linux下5種IO模型的小結
概述
接觸網絡編程,我們時常會與各種與IO相關的概念打交道: 同步(Synchronous)、異步(ASynchronous)、阻塞( blocking )和非阻塞(non-blocking) 。關于概念的區別在知乎上看到一位朋友( 鏈接 )打了一個比較形象的比喻:
你打電話問書店老板有沒有《分布式系統》這本書,如果是同步通信機制,書店老板會說,你稍等,”我查一下",然后開始查啊查,等查好了(可能 是5秒,也可能是一天)告訴你結果(返回結果)。而異步通信機制,書店老板直接告訴你我查一下啊,查好了打電話給你,然后直接掛電話了(不返回結果)。然 后查好了,他會主動打電話給你。在這里老板通過“回電”這種方式來回調。
你打電話問書店老板有沒有《分布式系統》這本書,你如果是阻塞式調用,你會一直把自己“掛起”,直到得到這本書有沒有的結果。如果是非阻塞式調用,你不管老板有沒有告訴你,你自己先一邊去玩了, 當然你也要偶爾過幾分鐘check一下老板有沒有返回結果。
在這里阻塞與非阻塞與是否同步異步無關。跟老板通過什么方式回答你結果無關。
同步與異步的主要區別就在于: 會不會導致 請求進程(或線程) 阻塞 。同步會使請求進程(或線程)阻塞而異步不會。
linux下有五種常見的IO模型,其中只有一種異步模型,其余皆為同步模型。如圖:
阻塞IO模型
阻塞IO模型是最常見的IO模型了,對于所有的“慢速設備”(socket、pipe、fifo、terminal)的IO默認的方式都是阻塞的 方式。阻塞就是進程放棄cpu,讓給其他進程使用cpu。進程阻塞最顯著的表現就是“進程睡眠了”。阻塞的時間通常取決于“數據”是否到來。
非阻塞IO模型
非阻塞IO就是設置IO相關的系統調用為non-blocaking,隨后進行的IO操作無論有沒有可用數據都會立即返回,并設置errno為 EWOULDBLOCK或者EAGAIN。我們可以通過主動check的方式(polling,輪詢)確保IO有效時,隨之進行相關的IO操作。當然這種 方式看起來就似乎不太靠譜,浪費了太多的CPU時間,用寶貴的CPU時間做輪詢太不靠譜兒了。圖示:
多路復用IO模型
多路復用是讓 阻塞 發生在我們的多路復用IO操作的系統調用上面,而不是我們真正去執行IO的系統調用。使用這個方式的好處就是可以同時監控多個用于IO的文件描述符。詳細的使用方式之前寫了一篇博客有提到: http://www.cnblogs.com/ittinybird/p/4574397.html
信號驅動IO模型
所謂信號驅動,就是利用信號機制,安裝信號SIGIO的處理函數(進行IO相關操作),通過監控文件描述符,當其就緒時,通知目標進程進行IO操作(signal handler)。具體使用方法之前博客也有說明: http://www.cnblogs.com/ittinybird/p/4574397.html
異步IO模型
Linux上異步IO有一組POSIX規定的接口,已aio開頭的幾個SYSCALL。如下:
int aio_read(struct aiocb *aiocbp);
int aio_write(struct aiocb *aiocbp);
ssize_t aio_return(struct aiocb *aiocbp);
使用時記得 Link with -lrt.參數看起來給人一種很簡潔的假象。其實相較于其他模型的參數一個也沒有少,只是放到了結構體里邊了。先看一下struct aiocb這個結構的原型吧,頭文件是”aio.h“。
struct aiocb
{
int aio_fildes; /* File desriptor. */
int aio_lio_opcode; /* Operation to be performed. */
int aio_reqprio; /* Request priority offset. */
volatile void *aio_buf; /* Location of buffer. */
size_t aio_nbytes; /* Length of transfer. */
struct sigevent aio_sigevent; /* Signal number and value. */
/* Internal members. */
struct aiocb *__next_prio;
int __abs_prio;
int __policy;
int __error_code;
__ssize_t __return_value;
#ifndef __USE_FILE_OFFSET64
__off_t aio_offset; /* File offset. */
char __pad[sizeof (__off64_t) - sizeof (__off_t)];
#else
__off64_t aio_offset; /* File offset. */
#endif
char __unused[32];
};其實雖然結構體足夠長,其實真正用到的也就前面那幾個參數,也沒那么復雜。具體用法不再贅述。具體可以參考以為網友的博文: http://blog.csdn.net/tq02h2a/article/details/3825114 。
下圖是關于異步IO模型的圖示:
參考
aio(7) - Linux manual page http://man7.org/linux/man-pages/man7/aio.7.html
sigevent(7) - Linux manual page http://man7.org/linux/man-pages/man7/sigevent.7.html
5張模型圖片出處 《UNIX網絡編程卷1》 史蒂文斯