Linux的內(nèi)核將所有外部設(shè)備都看做一個(gè)文件來操作疗涉,對一個(gè)文件的讀寫操作會調(diào)用內(nèi)核提供的系統(tǒng)命令,返回一個(gè)file descriptor(fd吟秩,文件描述符 )咱扣。而對一個(gè)socket的讀寫也會有相應(yīng)的描述符,稱為socketfd(socket描述符)峰尝,描述符就是一個(gè)數(shù)字偏窝,它指向內(nèi)核中的一個(gè)結(jié)構(gòu)體(文件路徑,數(shù)據(jù)區(qū)等一些屬性)武学。
根據(jù)UNIX網(wǎng)絡(luò)編程對I/O模型的分類祭往,UNIX提供了5種I/O模型,分別如下:
1火窒、阻塞I/O模型
最常用的I/O模型就是阻塞I/O模型硼补,缺省情形下,所有文件操作都是阻塞的熏矿。我們以套接字接口為例來講解此模型已骇。在進(jìn)程空間中調(diào)用recvfrom拣展,其系統(tǒng)調(diào)用直到數(shù)據(jù)包到達(dá)且被復(fù)制到應(yīng)用進(jìn)程的緩沖區(qū)中或者發(fā)生錯(cuò)誤時(shí)才返回娃圆,在此期間一直會等待,進(jìn)程在從調(diào)用recvfrom開始到它返回的整段時(shí)間內(nèi)都是被阻塞的瓣赂,因此被稱為阻塞I/O模型慧域。如下圖所示:
2鲤竹、非阻塞I/O模型
rcvfrom從應(yīng)用層到內(nèi)核的時(shí)候,如果該緩沖區(qū)沒有數(shù)據(jù)的話昔榴,就直接返回一個(gè)EWOULDBLOCK錯(cuò)誤辛藻,一般都對非阻塞I/O模型進(jìn)行輪詢檢查這個(gè)狀態(tài),看內(nèi)核是不是有數(shù)據(jù)到來互订。如下圖所示:
3吱肌、I/O復(fù)用模型
Linux提供select/poll(I/O復(fù)用模型會用到select或者poll函數(shù),這兩個(gè)函數(shù)也會使進(jìn)程阻塞仰禽,但是和阻塞I/O所不同的是氮墨,這兩個(gè)函數(shù)可以同時(shí)阻塞多個(gè)I/O操作纺蛆。而且可以同時(shí)對多個(gè)讀操作,多個(gè)寫操作的I/O函數(shù)進(jìn)行檢測勇边,直到有數(shù)據(jù)可讀或可寫時(shí)犹撒,才真正調(diào)用I/O操作函數(shù)),進(jìn)程通過將一個(gè)或多個(gè)fd傳遞給select或poll系統(tǒng)調(diào)用粒褒,阻塞在select操作上,這樣select/poll可以幫我們偵測多fd是否處于就緒狀態(tài)诚镰。select/poll是順序掃描fd是否就緒奕坟,而且支持的fd數(shù)量有限,因此它的使用受到了一些制約清笨。Linux還提供了一個(gè)epoll系統(tǒng)調(diào)用月杉,epoll使用基于事件驅(qū)動方式代替順序掃描,因此性能更高抠艾。當(dāng)有fd就緒時(shí)苛萎,立即回調(diào)函數(shù)rollback。如下圖所示:
4检号、信號驅(qū)動I/O模型
首先開啟套接口信號驅(qū)動I/O功能腌歉,并通過系統(tǒng)調(diào)用sigaction執(zhí)行一個(gè)信號處理函數(shù)(此系統(tǒng)調(diào)用立即返回,進(jìn)程繼續(xù)工作齐苛,它是非阻塞的)翘盖。當(dāng)數(shù)據(jù)準(zhǔn)備就緒時(shí),就為該進(jìn)程生成一個(gè)SIGIO信號凹蜂,通過信號回調(diào)通知應(yīng)用程序調(diào)用recvfrom來讀取數(shù)據(jù)馍驯,并通知主循環(huán)函數(shù)處理數(shù)據(jù)。如下圖所示:
5玛痊、異步I/O
告知內(nèi)核啟動某個(gè)操作汰瘫,并讓內(nèi)核在整個(gè)操作完成后(包括將數(shù)據(jù)從內(nèi)核復(fù)制到用戶自己的緩沖區(qū))通知我們。這種模型與信號驅(qū)動模型的主要區(qū)別是:信號驅(qū)動I/O由內(nèi)核通知我們何時(shí)可以開始一個(gè)I/O操作擂煞;異步I/O模型由內(nèi)核通知我們I/O操作何時(shí)已經(jīng)完成混弥。
如果想要了解更多的UNIX系統(tǒng)網(wǎng)絡(luò)編程知識,可以閱讀《UNIX網(wǎng)絡(luò)編程》颈娜,里面有非常詳細(xì)的原理和API介紹剑逃。對于大多數(shù)Java程序員來說,不需要了解網(wǎng)絡(luò)編程的底層細(xì)節(jié)官辽,大家只需要有個(gè)概念蛹磺,知道對于操作系統(tǒng)而言,底層是支持異步I/O通信的同仆,只不過在很長一段時(shí)間Java并沒有提供異步I/O通信的類庫萤捆,導(dǎo)致很多原生的Java程序員對這塊兒比較陌生。當(dāng)你了解了網(wǎng)絡(luò)編程的基礎(chǔ)知識后,理解Java的NIO類庫就會更加容易一些俗或。
參考資料
Netty權(quán)威指南 第二版