如何實現(xiàn)IO的底層原理問題
最近在看 JAVA NIO 的相關(guān)知識,了解一下IO的底層實現(xiàn)原理。
IO涉及到的底層的概念大致如下:
1) 緩沖區(qū)操作。2) 內(nèi)核空間與用戶空間。3) 虛擬內(nèi)存。4) 分頁技術(shù)。
一,虛擬存儲器
虛擬存儲器是硬件異常(缺頁異常)、硬件地址翻譯、主存、磁盤文件和內(nèi)核軟件的***交互,它為每個進(jìn)程提供了一個大的、一致的和私有的地址空間。
虛擬存儲器的三大能力:①將主存看成是一個存儲在磁盤上的地址空間的高速緩存。②為每個進(jìn)程提供了一個一致的地址空間。③保護(hù)每個進(jìn)程的地址空間不被其他進(jìn)程破壞。
虛擬內(nèi)存的兩大好處:① 一個以上的虛擬地址可指向同一個物理內(nèi)存地址。② 虛擬內(nèi)存空間可大于實際可用的硬件內(nèi)存。
二,用戶空間與內(nèi)核空間
設(shè)虛擬地址為32位,那么虛擬地址空間的范圍為0~4G。操作系統(tǒng)將這4G分為二部分,將***的1G字節(jié)(虛擬地址范圍為:0xC0000000-0xFFFFFFFF)供內(nèi)核使用,稱為內(nèi)核空間。而將較低的3G字節(jié)供各個進(jìn)程使用,稱為用戶空間。
每個進(jìn)程可以通過系統(tǒng)調(diào)用進(jìn)入內(nèi)核,因為內(nèi)核是由所有的進(jìn)程共享的。對于每一個具體的進(jìn)程,它看到的都是4G大小的虛擬地址空間,即相當(dāng)于每個進(jìn)程都擁有一個4G大小的虛擬地址空間。
三,IO操作
一般IO緩沖區(qū)操作:
1) 用戶進(jìn)程使用read()系統(tǒng)調(diào)用,要求其用戶空間的緩沖區(qū)被填滿。
2) 內(nèi)核向磁盤控制器硬件發(fā)命令,要求從磁盤讀入數(shù)據(jù)。
3) 磁盤控制器以DMA方式(數(shù)據(jù)不經(jīng)過CPU)把數(shù)據(jù)復(fù)制到內(nèi)核緩沖區(qū)。
4) 內(nèi)核將數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到用戶進(jìn)程發(fā)起read()調(diào)用時指定的用戶緩沖區(qū)。
從上圖可以看出:磁盤中的數(shù)據(jù)是先讀取到內(nèi)核的緩沖區(qū)中。然后再從內(nèi)核的緩沖區(qū)復(fù)制到用戶的緩沖區(qū)。為什么會這樣呢?
因為用戶空間的進(jìn)程是不能直接硬件的(操作磁盤控制器)。磁盤是基于塊存儲的硬件設(shè)備,它一次操作固定大小的塊,而用戶請求請求的可能是任意大小的數(shù)據(jù)塊。因此,將數(shù)據(jù)從磁盤傳遞到用戶空間,由內(nèi)核負(fù)責(zé)數(shù)據(jù)的分解、再組合。
內(nèi)存映射IO:就是復(fù)用一個以上的虛擬地址可以指向同一個物理內(nèi)存地址。將內(nèi)核空間的緩沖區(qū)地址(內(nèi)核地址空間)映射到物理內(nèi)存地址區(qū)域,將用戶空間的緩沖區(qū)地址(用戶地址空間)也映射到相同的物理內(nèi)存地址區(qū)域。從而數(shù)據(jù)不需要從內(nèi)核緩沖區(qū)映射的物理內(nèi)存地址移動到用戶緩沖區(qū)映射的物理內(nèi)存地址了。
要求:①用戶緩沖區(qū)與內(nèi)核緩沖區(qū)必須使用相同的頁大小對齊。②緩沖區(qū)的大小必須是磁盤控制器塊大小(512字節(jié)磁盤扇區(qū))的倍數(shù)---因為磁盤是基于塊存儲的硬件設(shè)備,一次只能操作固定大小的數(shù)據(jù)塊。
用戶緩沖區(qū)按頁對齊,會提高IO的效率---這也是為什么在JAVA中new 一個字節(jié)數(shù)組時,指定的大小為2的倍數(shù)(4096)的原因吧。
四,JAVA中的IO,本質(zhì)上是把數(shù)據(jù)移進(jìn)或者移出緩沖區(qū)。
read()和write()系統(tǒng)調(diào)用完成的作用是:把內(nèi)核緩沖區(qū)映射的物理內(nèi)存空間中的數(shù)據(jù) 拷貝到 用戶緩沖區(qū)映射的物理內(nèi)存空間中。
因此,當(dāng)使用內(nèi)存映射IO時,可視為:用戶進(jìn)程直接把文件數(shù)據(jù)當(dāng)作內(nèi)存,也就不需要使用read()或write()系統(tǒng)調(diào)用了。
當(dāng)發(fā)起一個read()系統(tǒng)調(diào)用時,根據(jù)待讀取的數(shù)據(jù)的位置生成一個虛擬地址(用戶進(jìn)程使用的是虛擬地址),由MMU轉(zhuǎn)換成物理地址,若內(nèi)核中沒有相應(yīng)的數(shù)據(jù),產(chǎn)生一個缺頁請求,內(nèi)核負(fù)責(zé)頁面調(diào)入從而將數(shù)據(jù)從磁盤讀取到內(nèi)核緩沖區(qū)映射的物理內(nèi)存中。對用戶程序而言,這一切都是在不知不覺中進(jìn)行。
總之,從根本上講數(shù)據(jù)從磁盤裝入內(nèi)存是以頁為單位通過分頁技術(shù)裝入內(nèi)存的。
五,JAVA NIO中的直接緩存和非直接緩存
直接緩存:不是分配于堆上的存儲,位于JVM之外,它不受JAVA的GC管理,相當(dāng)于內(nèi)核緩沖區(qū)。非直接緩存:建立在JAVA堆上的緩存,受JVM管理,相當(dāng)于用戶緩沖區(qū)。
根據(jù)上面第三點,將直接緩存中的數(shù)據(jù)寫入通道的速度要快于非直接緩存。因為,連接到通道的另一端是文件(磁盤,F(xiàn)ileChannel)或者網(wǎng)絡(luò)(Socket通道),這些都是某種形式上的硬件。那么,對于非直接緩存而言,數(shù)據(jù)從緩沖區(qū)傳遞到硬件,要經(jīng)過內(nèi)核緩沖區(qū)中轉(zhuǎn)。而對于直接緩存而言,就不需要了,因為直接緩存已經(jīng)直接映射到內(nèi)核緩沖區(qū)了。