在涉及到IO的開(kāi)發(fā)中勾习,我們經(jīng)城缮簦看到零拷貝(zero copy)、內(nèi)存映射(memroy map, 以下簡(jiǎn)稱(chēng)mmap)等技術(shù)被用于提高IO效率英岭,本文將介紹這兩種技術(shù)的基本原理眼滤,說(shuō)明它們是如何提高IO效率的诅需。
相關(guān)概念
Zero copy和mmap涉及到操作系統(tǒng)中的一些基本概念荧库,在了解它們的工作機(jī)制前分衫,我們先來(lái)復(fù)習(xí)一下這些概念。
虛擬內(nèi)存(virtual memory space)
進(jìn)程對(duì)內(nèi)存的讀寫(xiě)不是直接使用物理內(nèi)存地址牵现,而是基于虛擬地址瞎疼。
每個(gè)進(jìn)程運(yùn)行時(shí)壁畸,操作系統(tǒng)都會(huì)為其創(chuàng)建一個(gè)私有的虛擬內(nèi)存捏萍,存放進(jìn)程運(yùn)行時(shí)代碼和數(shù)據(jù)。虛擬內(nèi)存大小取決與操作系統(tǒng)和所在機(jī)器的體系結(jié)構(gòu)走敌,對(duì)于32機(jī)器來(lái)說(shuō)逗噩,空間大小為4g给赞。
操作系統(tǒng)通過(guò)內(nèi)存管理機(jī)制,將虛擬內(nèi)存映射到物理內(nèi)存残邀。
虛擬內(nèi)存使得操作系統(tǒng)可以同時(shí)支持多個(gè)運(yùn)行進(jìn)程安全共享物理內(nèi)存,防止進(jìn)程之間的不安全讀寫(xiě)驱闷。
User space vs kernel space
虛擬內(nèi)存分為兩部分:用戶(hù)空間和內(nèi)核空間空另。用戶(hù)空間存放用戶(hù)代碼和用戶(hù)數(shù)據(jù)蹋砚;內(nèi)核空間存放操作系統(tǒng)代碼。
前面說(shuō)過(guò)循榆,每個(gè)進(jìn)程有自己私有的虛擬內(nèi)存秧饮,不同進(jìn)程的虛擬內(nèi)存中的相同的地址泽篮,被映射到物理內(nèi)存中的不同位置帽撑。但是內(nèi)核空間是個(gè)例外,所有進(jìn)程是共享內(nèi)核空間的历恐,也就是對(duì)不同進(jìn)程來(lái)說(shuō)弱贼,它們內(nèi)核空間內(nèi)的內(nèi)容磷蛹、地址映射實(shí)際上都是相同的践图。
缺頁(yè)中斷(page fault)
操作系統(tǒng)為每個(gè)進(jìn)程的虛擬內(nèi)存和物理內(nèi)存之間建立了一張映射表蒸辆,需要注意的是峭状,虛擬內(nèi)存中的內(nèi)容只會(huì)一部分被裝載到物理內(nèi)存中。
當(dāng)進(jìn)程訪(fǎng)問(wèn)的虛擬地址對(duì)應(yīng)的內(nèi)容不在物理內(nèi)存時(shí)馆铁,操作系統(tǒng)會(huì)觸發(fā)一個(gè)缺頁(yè)中斷莱睁,將物理內(nèi)存中不用的內(nèi)容暫時(shí)置換到磁盤(pán)嘿期,將需要的內(nèi)容讀取道物理內(nèi)存掂铐。通過(guò)這種管理模式罕拂,我們可以在同時(shí)運(yùn)行多個(gè)進(jìn)程的情況下揍异,讓每個(gè)進(jìn)程覺(jué)得自己在獨(dú)享整個(gè)內(nèi)存空間。
User mode vs kernel mode
操作系統(tǒng)至少為進(jìn)程提供兩種運(yùn)行模式:用戶(hù)態(tài)(usermode)衷掷、內(nèi)核態(tài)(kernel mode)。不同的模式柿菩,實(shí)際對(duì)應(yīng)著不同的運(yùn)行權(quán)限戚嗅。
用戶(hù)態(tài)的進(jìn)程,只能訪(fǎng)問(wèn)用戶(hù)空間枢舶,不能直接訪(fǎng)問(wèn)設(shè)備懦胞。而內(nèi)核態(tài)的進(jìn)程,可以訪(fǎng)問(wèn)用戶(hù)空間和內(nèi)核空間祟辟,可以訪(fǎng)問(wèn)硬件設(shè)備医瘫。
進(jìn)程模式切換
一個(gè)進(jìn)程可以通過(guò)系統(tǒng)調(diào)用在用戶(hù)態(tài)和內(nèi)核態(tài)之間切換,此外旧困,中斷、異常等機(jī)制也可以讓進(jìn)程沖用戶(hù)態(tài)切換到內(nèi)核態(tài)稼锅。
這里簡(jiǎn)單說(shuō)明下系統(tǒng)調(diào)用的過(guò)程吼具;
- 用戶(hù)態(tài)進(jìn)程準(zhǔn)備好系統(tǒng)調(diào)用的參數(shù);
- 進(jìn)程執(zhí)行
trap
指令 - cpu自動(dòng)切換到內(nèi)核態(tài)
- cpu讀取進(jìn)程事先設(shè)置的系統(tǒng)調(diào)用參數(shù)
- 執(zhí)行指定的系統(tǒng)調(diào)用
- 結(jié)束后矩距,進(jìn)程重新被設(shè)置為用戶(hù)態(tài)
工作過(guò)程分析
假定我們現(xiàn)在要實(shí)現(xiàn)一個(gè)讀取文件拗盒,在內(nèi)存中處理,然后將其輸出的需求锥债,我們看看在不同的實(shí)現(xiàn)方式中陡蝇,底層究竟是如何工作的。
普通實(shí)現(xiàn)(read + write)
首先哮肚,我們使用常規(guī)的文件io操作實(shí)現(xiàn)需求登夫。
- 進(jìn)程通過(guò)系統(tǒng)調(diào)用讀取數(shù)據(jù)
- 進(jìn)程切換到內(nèi)核態(tài),通知設(shè)備進(jìn)行讀取
- 設(shè)備準(zhǔn)備好的數(shù)據(jù)傳送到內(nèi)核空間
- 接收到數(shù)據(jù)后允趟,內(nèi)核態(tài)進(jìn)程將數(shù)據(jù)從內(nèi)核空間拷貝到用戶(hù)空間
- 進(jìn)程切換回用戶(hù)態(tài)恼策,繼續(xù)執(zhí)行用戶(hù)空間的代碼:處理數(shù)據(jù)
- 進(jìn)程通過(guò)系統(tǒng)調(diào)用輸出數(shù)據(jù)到設(shè)備
- 進(jìn)程切換到內(nèi)核態(tài),把數(shù)據(jù)從用戶(hù)空間拷貝到內(nèi)核空間潮剪,通知設(shè)備進(jìn)行輸出操作
- 設(shè)備完成任務(wù)后涣楷,進(jìn)程再次從內(nèi)核態(tài)切換回用戶(hù)態(tài)
在上面的過(guò)程中,涉及到4次進(jìn)程模式切換抗碰,兩次內(nèi)存拷貝狮斗。這些操作對(duì)性能會(huì)造成一定影響。
sendfile
接下來(lái)弧蝇,我們通過(guò)sendfile
調(diào)用碳褒,減少上述過(guò)程中的內(nèi)存拷貝折砸,實(shí)現(xiàn)零拷貝。
通過(guò)sendfile
骤视,我們看到進(jìn)程模式切換從4次減少到2次鞍爱,拷貝從2次減少到1次。
但是sendfile
只能完成文件的拷貝操作专酗,無(wú)法處理文件內(nèi)容睹逃,mmap
則可以幫助我們實(shí)現(xiàn)零拷貝下的處理。
mmap
通過(guò)調(diào)用memory map祷肯,我們讓操作系統(tǒng)把文件的內(nèi)容映射到內(nèi)存沉填,對(duì)內(nèi)存的讀寫(xiě)將關(guān)聯(lián)到對(duì)應(yīng)的文件。而應(yīng)用通過(guò)訪(fǎng)問(wèn)用戶(hù)空間操作這部分內(nèi)存佑笋,避免了內(nèi)存拷貝操作翼闹。
對(duì)于內(nèi)存映射,有些地方容易被誤解蒋纬,這里說(shuō)明一下猎荠。
內(nèi)存映射是文件到內(nèi)存空間的映射
對(duì)于應(yīng)用來(lái)說(shuō),和文件建立映射關(guān)系的是虛擬地址空間蜀备,而不是物理內(nèi)存或者Heap关摇。
當(dāng)我們建立一個(gè)2g大小的映射時(shí),并不是在heap碾阁,更不是在物理內(nèi)存中分配了這么大的空間输虱,僅僅是在虛擬地址空間中劃出了這么大一個(gè)區(qū)域而已。
應(yīng)用訪(fǎng)問(wèn)內(nèi)存映射區(qū)域時(shí)脂凶,操作系統(tǒng)會(huì)把虛擬的地址映射成真正的物理內(nèi)存地址和底層文件的偏移量宪睹。如果應(yīng)用訪(fǎng)問(wèn)的虛擬地址對(duì)應(yīng)的文件內(nèi)容尚未被裝入內(nèi)存,操作系統(tǒng)通過(guò)缺頁(yè)中斷蚕钦,將內(nèi)存中的部分內(nèi)容交換出去亭病,騰出空間將文件的內(nèi)容讀取到內(nèi)存。
內(nèi)存映射對(duì)性能的提升是有條件的
通過(guò)內(nèi)存映射訪(fǎng)問(wèn)文件冠桃,雖然減少了內(nèi)存拷貝命贴,減少了系統(tǒng)調(diào)用引起的進(jìn)程模式切換,但是過(guò)程中需要承擔(dān)缺頁(yè)中斷的負(fù)擔(dān)食听。
對(duì)于小文件的讀取胸蛛,或者對(duì)于append模式的文件讀寫(xiě),內(nèi)存映射的性能未必優(yōu)于普通io操作樱报。只有對(duì)大文件的隨機(jī)訪(fǎng)問(wèn)葬项,內(nèi)存映射才可能有明顯優(yōu)勢(shì),不過(guò)這仍然需要更具體的分析和進(jìn)一步的的benchmark測(cè)試迹蛤。