㈠ linux鐨刴map鏂囦歡鍐呭瓨鏄犲皠鏈哄埗鏄浠涔堬紵
鍏變韓鍐呭瓨鍏佽鎬袱涓鎴栧氫釜榪涚▼鍏變韓涓緇欏畾鐨勫瓨鍌ㄥ尯錛屽洜涓烘暟鎹涓嶉渶瑕佹潵鍥炲嶅埗錛屾墍浠ユ槸鏈蹇鐨勪竴縐嶈繘紼嬮棿閫氫俊鏈哄埗銆傚叡浜鍐呭瓨鍙浠ラ氳繃mmap()鏄犲皠鏅閫氭枃浠訛紙鐗規畩鎯呭喌涓嬭繕鍙浠ラ噰鐢ㄥ尶鍚嶆槧灝勶級鏈哄埗瀹炵幇錛屼篃鍙浠ラ氳繃緋葷粺V鍏變韓鍐呭瓨鏈哄埗瀹炵幇銆傚簲鐢ㄦ帴鍙e拰鍘熺悊寰堢畝鍗曪紝鍐呴儴鏈哄埗澶嶆潅銆備負浜嗗疄鐜版洿瀹夊叏閫氫俊錛屽線寰榪樹笌淇″彿鐏絳夊悓姝ユ満鍒跺叡鍚屼嬌鐢ㄣ俶map鐨勬満鍒跺傦細灝辨槸鍦ㄧ佺洏涓婂緩絝嬩竴涓鏂囦歡錛屾瘡涓榪涚▼瀛樺偍鍣ㄩ噷闈錛屽崟鐙寮杈熶竴涓絀洪棿鏉ヨ繘琛屾槧灝勩傚傛灉澶氳繘紼嬬殑璇濓紝閭d箞涓嶄細瀵瑰疄闄呯殑鐗╃悊瀛樺偍鍣錛堜富瀛橈級娑堣楀お澶с俿hm鐨勬満鍒訛細姣忎釜榪涚▼鐨勫叡浜鍐呭瓨閮界洿鎺ユ槧灝勫埌瀹為檯鐗╃悊瀛樺偍鍣ㄩ噷闈銆
1銆乵map淇濆瓨鍒板疄闄呯‖鐩橈紝瀹為檯瀛樺偍騫舵病鏈夊弽鏄犲埌涓誨瓨涓娿備紭鐐癸細鍌ㄥ瓨閲忓彲浠ュ緢澶э紙澶氫簬涓誨瓨錛夛紱緙虹偣錛氳繘紼嬮棿璇誨彇鍜屽啓鍏ラ熷害瑕佹瘮涓誨瓨鐨勮佹參銆
2銆乻hm淇濆瓨鍒扮墿鐞嗗瓨鍌ㄥ櫒錛堜富瀛橈級錛屽疄闄呯殑鍌ㄥ瓨閲忕洿鎺ュ弽鏄犲埌涓誨瓨涓娿備紭鐐癸紝榪涚▼闂磋塊棶閫熷害錛堣誨啓錛夋瘮紓佺洏瑕佸揩錛涚己鐐癸紝鍌ㄥ瓨閲忎笉鑳介潪甯稿ぇ錛堝氫簬涓誨瓨錛変嬌鐢ㄤ笂鐪嬶細濡傛灉鍒嗛厤鐨勫瓨鍌ㄩ噺涓嶅ぇ錛岄偅涔堜嬌鐢╯hm錛涘傛灉瀛樺偍閲忓ぇ錛岄偅涔堜嬌鐢╩map銆
㈡ 【深入淺出Linux】關於mmap的解析
看這篇文章之前需要知道一個概念
虛擬內存系統通過將虛擬內存分割為稱作虛擬頁(Virtual Page,VP)大小固定的塊,一般情況下,每個虛擬頁的大小默認是4096位元組。同樣的,物理內存也被分割為物理頁(Physical Page,PP),也為4096位元組。
在LINUX中我們可以使用mmap用來在進程虛擬內存地址空間中分配地址空間,創建和物理內存的映射關系。
映射關系可以分為兩種
1、文件映射
磁碟文件映射進程的虛擬地址空間,使用文件內容初始化物理內存。
2、匿名映射
初始化全為0的內存空間。
而對於映射關系是否共享又分為
1、私有映射(MAP_PRIVATE)
多進程間數據共享,修改不反應到磁碟實際文件,是一個-on-write(寫時復制)的映射方式。
2、共享映射(MAP_SHARED)
多進程間數據共享,修改反應到磁碟實際文件中。
因此總結起來有4種組合
1、私有文件映射
多個進程使用同樣的物理內存頁進行初始化,但是各個進程對內存文件的修改不會共享,也不會反應到物理文件中
2、私有匿名映射
mmap會創建一個新的映射,各個進程不共享,這種使用主要用於分配內存(malloc分配大內存會調用mmap)。
例如開辟新進程時,會為每個進程分配虛擬的地址空間,這些虛擬地址映射的物理內存空間各個進程間讀的時候共享,寫的時候會-on-write。
3、共享文件映射
多個進程通過虛擬內存技術共享同樣的物理內存空間,對內存文件 的修改會反應到實際物理文件中,他也是進程間通信(IPC)的一種機制。
4、共享匿名映射
這種機制在進行fork的時候不會採用寫時復制,父子進程完全共享同樣的物理內存頁,這也就實現了父子進程通信(IPC).
這里值得注意的是,mmap只是在虛擬內存分配了地址空間,只有在第一次訪問虛擬內存的時候才分配物理內存。
在mmap之後,並沒有在將文件內容載入到物理頁上,只上在虛擬內存中分配了地址空間。當進程在訪問這段地址時,通過查找頁表,發現虛擬內存對應的頁沒有在物理內存中緩存,則產生"缺頁",由內核的缺頁異常處理程序處理,將文件對應內容,以頁為單位(4096)載入到物理內存,注意是只載入缺頁,但也會受操作系統一些調度策略影響,載入的比所需的多。
1.write
因為物理內存是有限的,mmap在寫入數據超過物理內存時,操作系統會進行頁置換,根據淘汰演算法,將需要淘汰的頁置換成所需的新頁,所以mmap對應的內存是可以被淘汰的(若內存頁是"臟"的,則操作系統會先將數據回寫磁碟再淘汰)。這樣,就算mmap的數據遠大於物理內存,操作系統也能很好地處理,不會產生功能上的問題。
2.read
從圖中可以看出,mmap要比普通的read系統調用少了一次的過程。因為read調用,進程是無法直接訪問kernel space的,所以在read系統調用返回前,內核需要將數據從內核復制到進程指定的buffer。但mmap之後,進程可以直接訪問mmap的數據(page cache)。
測試結果來源於: 深入剖析mmap-從三個關鍵問題說起
1.讀性能分析
場景:對2G的文件進行順序寫入
可以看到mmap在100byte寫入時已經基本達到最大寫入性能,而write調用需要在4096(也就是一個page size)時,才能達到最大寫入性能。
從測試結果可以看出,在寫小數據時,mmap會比write調用快,但在寫大數據時,反而沒那麼快。
2.寫性能分析
場景:對2G的文件進行順序讀取(為了避免磁碟對測試的影響,2G文件都緩存在pagecache中)
由上可以看出,在read上面,mmap的性能還是非常好的。
優點如下:
1、對文件的讀取操作跨過了頁緩存,減少了數據的拷貝次數,用內存讀寫取代I/O讀寫,提高了文件讀取效率。
2、實現了用戶空間和內核空間的高效交互方式。兩空間的各自修改操作可以直接反映在映射的區域內,從而被對方空間及時捕捉。
3、提供進程間共享內存及相互通信的方式。不管是父子進程還是無親緣關系的進程,都可以將自身用戶空間映射到同一個文件或匿名映射到同一片區域。從而通過各自對映射區域的改動,達到進程間通信和進程間共享的目的。同時,如果進程A和進程B都映射了區域C,當A第一次讀取C時通過缺頁從磁碟復制文件頁到內存中;但當B再讀C的相同頁面時,雖然也會產生缺頁異常,但是不再需要從磁碟中復制文件過來,而可直接使用已經保存在內存中的文件數據。
4、可用於實現高效的大規模數據傳輸。內存空間不足,是制約大數據操作的一個方面,解決方案往往是藉助硬碟空間協助操作,補充內存的不足。但是進一步會造成大量的文件I/O操作,極大影響效率。這個問題可以通過mmap映射很好的解決。換句話說,但凡是需要用磁碟空間代替內存的時候,mmap都可以發揮其功效。
缺點如下:
1.文件如果很小,是小於4096位元組的,比如10位元組,由於內存的最小粒度是頁,而進程虛擬地址空間和內存的映射也是以頁為單位。雖然被映射的文件只有10位元組,但是對應到進程虛擬地址區域的大小需要滿足整頁大小,因此mmap函數執行後,實際映射到虛擬內存區域的是4096個位元組,11~4096的位元組部分用零填充。因此如果連續mmap小文件,會浪費內存空間。
3.如果更新文件的操作很多,會觸發大量的臟頁回寫及由此引發的隨機IO上。所以在隨機寫很多的情況下,mmap方式在效率上不一定會比帶緩沖區的一般寫快。
㈢ linux為什麼主要採用分頁機制來實現虛擬存儲管理
1 分頁機制
在虛擬內存中,頁表是個映射表的概念, 即從進程能理解的線性地址(linear address)映射到存儲器上的物理地址(phisical address).
很顯然,這個頁表是需要常駐內存的東西, 以應對頻繁的查詢映射需要(實際上,現代支持VM的處理器都有一個叫TLB的硬體級頁表緩存部件,本文不討論)。
1.1 為什麼使用多級頁表來完成映射
但是為什麼要使用多級頁表來完成映射呢?
用來將虛擬地址映射到物理地址的數據結構稱為頁表, 實現兩個地址空間的關聯最容易的方式是使用數組, 對虛擬地址空間中的每一頁, 都分配一個數組項. 該數組指向與之關聯的頁幀, 但這會引發一個問題, 例如, IA-32體系結構使用4KB大小的頁, 在虛擬地址空間為4GB的前提下, 則需要包含100萬項的頁表. 這個問題在64位體系結構下, 情況會更加糟糕. 而每個進程都需要自身的頁表, 這回導致系統中大量的所有內存都用來保存頁表.
設想一個典型的32位的X86系統,它的虛擬內存用戶空間(user space)大小為3G, 並且典型的一個頁表項(page table entry, pte)大小為4 bytes,每一個頁(page)大小為4k bytes。那麼這3G空間一共有(3G/4k=)786432個頁面,每個頁面需要一個pte來保存映射信息,這樣一共需要786432個pte!
如何存儲這些信息呢?一個直觀的做法是用數組來存儲,這樣每個頁能存儲(4k/4=)1K個,這樣一共需要(786432/1k=)768個連續的物理頁面(phsical page)。而且,這只是一個進程,如果要存放所有N個進程,這個數目還要乘上N! 這是個巨大的數目,哪怕內存能提供這樣數量的空間,要找到連續768個連續的物理頁面在系統運行一段時間後碎片化的情況下,也是不現實的。
為減少頁表的大小並容許忽略不需要的區域, 計算機體系結構的涉及會將虛擬地址分成多個部分. 同時虛擬地址空間的大部分們區域都沒有使用, 因而頁沒有關聯到頁幀, 那麼就可以使用功能相同但內存用量少的多的模型: 多級頁表
但是新的問題來了, 到底採用幾級頁表合適呢?
1.2 32位系統中2級頁表
從80386開始, intel處理器的分頁單元是4KB的頁, 32位的地址空間被分為3部分
單元
描述
頁目錄表Directory 最高10位
頁中間表Table 中間10位
頁內偏移 最低12位
即頁表被劃分為頁目錄表Directory和頁中間表Tabl兩個部分
此種情況下, 線性地址的轉換分為兩步完成.
第一步, 基於兩級轉換表(頁目錄表和頁中間表), 最終查找到地址所在的頁幀
第二步, 基於偏移, 在所在的頁幀中查找到對應偏移的物理地址
使用這種二級頁表可以有效的減少每個進程頁表所需的RAM的數量. 如果使用簡單的一級頁表, 那將需要高達220個頁表, 假設每項4B, 則共需要佔用220?4B=4MB的RAM來表示每個進程的頁表. 當然我們並不需要映射所有的線性地址空間(32位機器上線性地址空間為4GB), 內核通常只為進程實際使用的那些虛擬內存區請求頁表來減少內存使用量.
1.3 64位系統中的分頁
正常來說, 對於32位的系統兩級頁表已經足夠了, 但是對於64位系統的計算機, 這遠遠不夠.
首先假設一個大小為4KB的標准頁. 因為1KB覆蓋210個地址的范圍, 4KB覆蓋212個地址, 所以offset欄位需要12位.
這樣線性地址空間就剩下64-12=52位分配給頁中間表Table和頁目錄表Directory. 如果我們現在決定僅僅使用64位中的48位來定址(這個限制其實已經足夠了, 2^48=256TB, 即可達到256TB的定址空間). 剩下的48-12=36位被分配給Table和Directory欄位. 即使我們現在決定位兩個欄位各預留18位, 那麼每個進程的頁目錄和頁表都包含218個項, 即超過256000個項.
基於這個原因, 所有64位處理器的硬體分頁系統都使用了額外的分頁級別. 使用的級別取決於處理器的類型
平台名稱
頁大小
定址所使用的位數
分頁級別數
線性地址分級
alpha 8KB 43 3 10 + 10 + 10 + 13
ia64 4KB 39 3 9 + 9 + 9 + 12
ppc64 4KB 41 3 10 + 10 + 9 + 12
sh64 4KB 41 3 10 + 10 + 9 + 12
x86_64 4KB 48 4 9 + 9 + 9 + 9 + 12
㈣ /dev/mapper/rhel-root鏄浠涔
/dev/mapper/rhel-root
緋葷粺鍒╃敤Device mapper鏈哄埗寤虹珛浜嗕竴涓鍗風粍錛坴olume group錛孷G錛夈
Device mapper 鏄 Linux 2.6 鍐呮牳涓鎻愪緵鐨勪竴縐嶄粠閫昏緫璁懼囧埌鐗╃悊璁懼囩殑鏄犲皠妗嗘灦鏈哄埗錛屽湪璇ユ満鍒朵笅錛岀敤鎴瘋兘澶熷緢鏂逛究鐨勬牴鎹鑷宸辯殑闇瑕佸畾鍒跺疄鐜板瓨鍌ㄨ祫婧愮殑綆$悊絳栫暐錛屽綋鍓嶆瘮杈冩祦琛岀殑 Linux 涓嬬殑閫昏緫鍗風$悊鍣ㄥ LVM2錛圠inux Volume Manager 2 version)銆丒VMS(Enterprise Volume Management System)銆乨mraid(Device Mapper Raid Tool)絳夐兘鏄鍩轟簬璇ユ満鍒跺疄鐜扮殑銆傜悊瑙hユ満鍒舵槸榪涗竴姝ュ垎鏋愩佺悊瑙h繖浜涘嵎綆$悊鍣ㄧ殑瀹炵幇鍙婅捐$殑鍩虹銆傞氳繃鏈鏂囦篃鑳藉熻繘涓姝ョ悊瑙 Linux 緋葷粺鍧椾竴綰 IO鐨勮捐″拰瀹炵幇銆傘奓inux灝辮ヨ繖涔堝︺
Device Mapper 鏄 Linux2.6 鍐呮牳涓鏀鎸侀昏緫鍗風$悊鐨勯氱敤璁懼囨槧灝勬満鍒訛紝浠栦負瀹炵幇鐢ㄤ簬瀛樺偍璧勬簮綆$悊鐨勫潡璁懼囬┍鍔ㄦ彁渚涗簡涓涓楂樺害妯″潡鍖栫殑鍐呮牳鏋舵瀯銆