接觸網絡編程,我們時常會與各種與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.linuxidc.com/Linux/2015-07/120335.htm
信號驅動IO模型
所謂信號驅動,就是利用信號機制,安裝信號SIGIO的處理函數(進行IO相關操作),通過監控文件描述符,當其就緒時,通知目標進程進行IO操作(signal handler)。具體使用方法之前博客也有說明:http://www.linuxidc.com/Linux/2015-07/120335.htm
異步IO模型
Linux上異步IO有一組POSIX規定的接口,已aio開頭的幾個SYSCALL。如下:
int aio_read(struct aiocb *aiocbp); int aio_write(struct aiocb *aiocbp);參數看起來給人一種很簡潔的假象。其實相較於其他模型的參數一個也沒有少,只是放到了結構體裡邊了。先看一下struct aiocb這個結構的原型吧,頭文件是”aio.h“。
ssize_t aio_return(struct aiocb *aiocbp);
使用時記得 Link with -lrt.
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://www.linuxidc.com/Linux/2015-07/120336.htm。
下圖是關於異步IO模型的圖示: