(整理)用户空间_内核空间以及内存映射
內(nèi)核空間和用戶空間
??現(xiàn)代操作系統(tǒng)采用虛擬存儲器,對于32位操作系統(tǒng)而言,它的尋址空間(虛擬存儲空間)為4G(2的32次方)。操作系統(tǒng)的核心是內(nèi)核,獨立于普通的應(yīng)用程序,可以訪問受保護(hù)的內(nèi)存空間,也有訪問底層硬件設(shè)備的權(quán)限。為了保證用戶進(jìn)程不能直接操作內(nèi)核,保證內(nèi)核安全,操作系統(tǒng)將虛擬空間劃分為兩部分,一部分是內(nèi)核空間,一部分是用戶空間。針對Linux操作系統(tǒng),將最高的1G字節(jié)(從虛擬地址0xC0000000到0xFFFFFFFF)供內(nèi)核使用,稱為內(nèi)核空間,而較低的3G字節(jié)(從虛擬地址0x00000000到0xBFFFFFFF),供各個進(jìn)程使用,稱為用戶空間。每個進(jìn)程都可以通過系統(tǒng)調(diào)用進(jìn)入到內(nèi)核。其中在Linux系統(tǒng)中,進(jìn)程的用戶空間是獨立的,而內(nèi)核空間是共有的,進(jìn)程切換時,用戶空間切換,內(nèi)核空間不變。
??有了用戶空間和內(nèi)核空間的劃分后,整個linux內(nèi)部結(jié)構(gòu)可以分為三部分,從最底層到最上層依次是:硬件->內(nèi)核空間->用戶空間,如下圖所示:
用戶態(tài)和內(nèi)核態(tài)
??當(dāng)一個進(jìn)程執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時,稱進(jìn)行處于內(nèi)核運行態(tài)(內(nèi)核態(tài))。此時處理器處于特權(quán)級別最高的(0級)內(nèi)核代碼中執(zhí)行。當(dāng)進(jìn)程處于內(nèi)核態(tài)時,執(zhí)行的內(nèi)核代碼會使用當(dāng)前進(jìn)程的內(nèi)核棧。每個進(jìn)程都有自己的內(nèi)核棧。
??當(dāng)進(jìn)行在執(zhí)行用戶自己的代碼時,則稱其處于用戶運行態(tài)(用戶態(tài))。此時處理器在特權(quán)級最低的(3級)用戶代碼中運行。當(dāng)正在執(zhí)行用戶程序而突然被中斷程序中斷時,此時用戶程序也可象征性的稱為處于進(jìn)行的內(nèi)核態(tài),因為中斷處理程序使用當(dāng)前進(jìn)程的內(nèi)核棧。
聲明:以上兩部分整理自:http://www.cnblogs.com/Anker/p/3269106.html
邏輯地址、線性地址和物理地址
??在解釋高端內(nèi)存和內(nèi)存映射前,先復(fù)習(xí)一下什么是邏輯地址、線性地址和物理地址吧,大家要是知道的話就可以直接跳過。
邏輯地址
??邏輯地址(Logical Address)?是指由程序產(chǎn)生的和段相關(guān)的偏移地址部分。例如,你在進(jìn)行C語言指針編程中,能讀取指針變量本身值(&操作),實際上這個值就是邏輯地址,他是相對于你當(dāng)前進(jìn)程數(shù)據(jù)段的地址,不和絕對物理地址相干。只有在Intel實模式下,邏輯地址才和物理地址相等(因為實模式?jīng)]有分段或分頁機制,Cpu不進(jìn)行自動地址轉(zhuǎn)換);邏輯也就是在Intel保護(hù)模式下程序執(zhí)行代碼段限長內(nèi)的偏移地址(假定代碼段、數(shù)據(jù)段如果完全相同)。應(yīng)用程式員僅需和邏輯地址打交道,而分段和分頁機制對你來說是完全透明的,僅由系統(tǒng)編程人員涉及。應(yīng)用程式員雖然自己能直接操作內(nèi)存,那也只能在操作系統(tǒng)給你分配的內(nèi)存段操作。
線性地址
線性地址(Linear Address)?是邏輯地址到物理地址變換之間的中間層。程式代碼會產(chǎn)生邏輯地址,或說是段中的偏移地址,加上相應(yīng)段的基地址就生成了一個線性地址。如果啟用了分頁機制,那么線性地址能再經(jīng)變換以產(chǎn)生一個物理地址。若沒有啟用分頁機制,那么線性地址直接就是物理地址。Intel 80386的線性地址空間容量為4G(2的32次方即32根地址總線尋址)。
物理地址
物理地址(Physical Address)?是指出目前CPU外部地址總線上的尋址物理內(nèi)存的地址信號,是地址變換的最終結(jié)果地址。如果啟用了分頁機制,那么線性地址會使用頁目錄和頁表中的項變換成物理地址。如果沒有啟用分頁機制,那么線性地址就直接成為物理地址了。
虛擬地址
??虛擬內(nèi)存(Virtual Memory)是指計算機呈現(xiàn)出要比實際擁有的內(nèi)存大得多的內(nèi)存量。因此他允許程式員編制并運行比實際系統(tǒng)擁有的內(nèi)存大得多的程式。這使得許多大型項目也能夠在具有有限內(nèi)存資源的系統(tǒng)上實現(xiàn)。一個非常恰當(dāng)?shù)谋扔魇?#xff1a;你不必非常長的軌道就能讓一列火車從上海開到北京。你只需要足夠長的鐵軌(比如說3公里)就能完成這個任務(wù)。采取的方法是把后面的鐵軌即時鋪到火車的前面,只要你的操作足夠快并能滿足需求,列車就能象在一條完整的軌道上運行。這也就是虛擬內(nèi)存管理需要完成的任務(wù)。在Linux0.11內(nèi)核中,給每個程式(進(jìn)程)都劃分了總?cè)萘繛?4MB的虛擬內(nèi)存空間。因此程式的邏輯地址范圍是0x0000000到0x4000000。有時我們也把邏輯地址稱為?虛擬地址。因為和虛擬內(nèi)存空間的概念類似,邏輯地址也是和實際物理內(nèi)存容量無關(guān)的。邏輯地址和物理地址的“差距”是0xC0000000,是由于虛擬地址->線性地址->物理地址映射正好差這個值。這個值是由操作系統(tǒng)指定的。機理?邏輯地址(或稱為虛擬地址)到線性地址是由CPU的段機制自動轉(zhuǎn)換的。如果沒有開啟分頁管理,則線性地址就是物理地址。如果開啟了分頁管理,那么系統(tǒng)程式需要參和線性地址到物理地址的轉(zhuǎn)換過程。具體是通過設(shè)置頁目錄表和頁表項進(jìn)行的。
聲明:以上部分摘自:http://blog.csdn.net/do2jiang/article/details/4512417
高端內(nèi)存
高端內(nèi)存的由來
??在傳統(tǒng)的Linux x86 32位系統(tǒng)中,內(nèi)核模塊的代碼或者線程訪問內(nèi)存時,代碼中的內(nèi)存地址都為邏輯地址,而對應(yīng)到真正的物理內(nèi)存地址時,還需要地址的一一映射。如果邏輯地址位0xC0000003,那么對應(yīng)的物理地址就是0x3,如果邏輯地址位0xC0000004,那么對應(yīng)的物理地址就是0x4,所以物理地址和邏輯地址的關(guān)系如下:
物理地址 = 邏輯地址 – 0xC0000000??根據(jù)上面的內(nèi)核地址空間的地址轉(zhuǎn)換關(guān)系,注意內(nèi)核的虛擬地址在“高端”,但是ta映射的物理內(nèi)存地址在低端。會發(fā)現(xiàn),內(nèi)核模塊能夠訪問的邏輯地址為0xC0000000-0xFFFFFFFF,對應(yīng)的物理地址為0x00000000-0x40000000,總共1G的內(nèi)存。也就是說如果計算機的總物理內(nèi)存大于1G,按照上面的映射關(guān)系,高于1G的部分,內(nèi)核就無法訪問到了。為了解決這種狀況,就出現(xiàn)了高端內(nèi)存一說。
??因為不能直接將內(nèi)和空間的1G內(nèi)存直接做一一映射,所以Linux內(nèi)核將內(nèi)核空間分成了三個部分,分別是:ZONE_DMA,ZONE_NORMAL和ZONE_HIGHMEM。這三個區(qū)域的內(nèi)存分配情況如下:
| ZONE_NORMAL | 16MB-896MB |
| ZONE_HIGHMEM | 896MB-結(jié)束(1G) |
對高端內(nèi)存的理解
??上一小節(jié)就說到高端內(nèi)存是用來解決內(nèi)核無法訪問大于1G內(nèi)存地址空間的問題的。那么具體是怎么實現(xiàn)的呢?總的來說非常簡單,當(dāng)內(nèi)核需要訪問高于1G的內(nèi)存空間的時候,例如內(nèi)核需要訪問0x50000000-0x500FFFFF這1MB內(nèi)存空間的時候,只需要在ZONE_HIGHMEM這一個區(qū)域內(nèi)臨時申請一個1MB的內(nèi)存空間,然后將其映射到上述需要訪問的內(nèi)存區(qū)域即可。當(dāng)內(nèi)核使用完后,釋放申請的1MB內(nèi)存空間便完成對高于1G內(nèi)存空間的訪問了。
內(nèi)存映射(mmap)
mmap基本概念
??mmap是一種內(nèi)存映射文件的方法,即將一個文件或者其它對象映射到進(jìn)程的地址空間,實現(xiàn)文件磁盤地址和進(jìn)程虛擬地址空間中一段虛擬地址的一一對映關(guān)系。實現(xiàn)這樣的映射關(guān)系后,進(jìn)程就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會自動回寫臟頁面到對應(yīng)的文件磁盤上,即完成了對文件的操作而不必再調(diào)用read,write等系統(tǒng)調(diào)用函數(shù)。相反,內(nèi)核空間對這段區(qū)域的修改也直接反映用戶空間,從而可以實現(xiàn)不同進(jìn)程間的文件共享。如下圖所示:
??由上圖可以看出,進(jìn)程的虛擬地址空間,由多個虛擬內(nèi)存區(qū)域構(gòu)成。虛擬內(nèi)存區(qū)域是進(jìn)程的虛擬地址空間中的一個同質(zhì)區(qū)間,即具有同樣特性的連續(xù)地址范圍。上圖中所示的text數(shù)據(jù)段(代碼段)、初始數(shù)據(jù)段、BSS數(shù)據(jù)段、堆、棧和內(nèi)存映射,都是一個獨立的虛擬內(nèi)存區(qū)域。而為內(nèi)存映射服務(wù)的地址空間處在堆棧之間的空余部分。
??linux內(nèi)核使用vm_area_struct結(jié)構(gòu)來表示一個獨立的虛擬內(nèi)存區(qū)域,由于每個不同質(zhì)的虛擬內(nèi)存區(qū)域功能和內(nèi)部機制都不同,因此一個進(jìn)程使用多個vm_area_struct結(jié)構(gòu)來分別表示不同類型的虛擬內(nèi)存區(qū)域。各個vm_area_struct結(jié)構(gòu)使用鏈表或者樹形結(jié)構(gòu)鏈接,方便進(jìn)程快速訪問,如下圖所示:
??vm_area_struct結(jié)構(gòu)中包含區(qū)域起始和終止地址以及其他相關(guān)信息,同時也包含一個vm_ops指針,其內(nèi)部可引出所有針對這個區(qū)域可以使用的系統(tǒng)調(diào)用函數(shù)。這樣,進(jìn)程對某一虛擬內(nèi)存區(qū)域的任何操作需要用要的信息,都可以從vm_area_struct中獲得。mmap函數(shù)就是要創(chuàng)建一個新的vm_area_struct結(jié)構(gòu),并將其與文件的物理磁盤地址相連。具體步驟請看下一節(jié)。
mmap內(nèi)存映射原理
mmap內(nèi)存映射的實現(xiàn)過程,總的來說可以分為三個階段:
進(jìn)程啟動映射過程,并在虛擬地址空間中為映射創(chuàng)建虛擬映射區(qū)域
- 進(jìn)程在用戶空間調(diào)用庫函數(shù)mmap,原型:void?mmap(void?start,?size_t?length,?int?prot,?int?flags, int?fd,?off_t?offset);
- 在當(dāng)前進(jìn)程的虛擬地址空間中,尋找一段空閑的滿足要求的連續(xù)的虛擬地址
- 為此虛擬區(qū)分配一個vm_area_struct結(jié)構(gòu),接著對這個結(jié)構(gòu)的各個域進(jìn)行了初始化
- 將新建的虛擬區(qū)結(jié)構(gòu)(vm_area_struct)插入進(jìn)程的虛擬地址區(qū)域鏈表或樹中
調(diào)用內(nèi)核空間的系統(tǒng)調(diào)用函數(shù)mmap(不同于用戶空間函數(shù)),實現(xiàn)文件物理地址和進(jìn)程虛擬地址的一一映射關(guān)系
- 為映射分配了新的虛擬地址區(qū)域后,通過待映射的文件指針,在文件描述符表中找到對應(yīng)的文件描述符,通過文件描述符,鏈接到內(nèi)核“已打開文件集”中該文件的文件結(jié)構(gòu)體(struct file),每個文件結(jié)構(gòu)體維護(hù)著和這個已打開文件相關(guān)各項信息。
- 通過該文件的文件結(jié)構(gòu)體,鏈接到file_operations模塊,調(diào)用內(nèi)核函數(shù)mmap,其原型為:int mmap(struct?file?filp,?struct?vm_area_struct?vma),不同于用戶空間庫函數(shù)。
- 內(nèi)核mmap函數(shù)通過虛擬文件系統(tǒng)inode模塊定位到文件磁盤物理地址。
- 通過remap_pfn_range函數(shù)建立頁表,即實現(xiàn)了文件地址和虛擬地址區(qū)域的映射關(guān)系。此時,這片虛擬地址并沒有任何數(shù)據(jù)關(guān)聯(lián)到主存中。
進(jìn)程發(fā)起對這片映射空間的訪問,引發(fā)缺頁異常,實現(xiàn)文件內(nèi)容到物理內(nèi)存(主存)的拷貝
注:前兩個階段僅在于創(chuàng)建虛擬區(qū)間并完成地址映射,但是并沒有將任何文件數(shù)據(jù)的拷貝至主存。真正的文件讀取是當(dāng)進(jìn)程發(fā)起讀或?qū)懖僮鲿r。
- 程的讀或?qū)懖僮髟L問虛擬地址空間這一段映射地址,通過查詢頁表,發(fā)現(xiàn)這一段地址并不在物理頁面上。因為目前只建立了地址映射,真正的硬盤數(shù)據(jù)還沒有拷貝到內(nèi)存中,因此引發(fā)缺頁異常。
- 缺頁異常進(jìn)行一系列判斷,確定無非法操作后,內(nèi)核發(fā)起請求調(diào)頁過程。
- 調(diào)頁過程先在交換緩存空間(swap?cache)中尋找需要訪問的內(nèi)存頁,如果沒有則調(diào)用nopage函數(shù)把所缺的頁從磁盤裝入到主存中。
- 之后進(jìn)程即可對這片主存進(jìn)行讀或者寫的操作,如果寫操作改變了其內(nèi)容,一定時間后系統(tǒng)會自動回寫臟頁面到對應(yīng)磁盤地址,也即完成了寫入到文件的過程。
注:修改過的臟頁面并不會立即更新回文件中,而是有一段時間的延遲,可以調(diào)用msync()來強制同步, 這樣所寫的內(nèi)容就能立即保存到文件里了。
聲明:以上一章內(nèi)容摘自:http://www.cnblogs.com/huxiao-tee/p/4660352.html
vm_struct和vm_area_struct
??關(guān)于vm_struct和vm_area_struct這兩個結(jié)構(gòu)體,需要簡單說明一下,vm_struct和vm_area_struct都是用于表示一片連續(xù)的虛擬地址空間的,但是映射到物理地址空間后可以是不連續(xù)的。其次,vm_area_struct表示的虛擬地址是給進(jìn)程使用的,而vm_struct表示的虛擬地址是給內(nèi)核使用的。從上面的內(nèi)容可以知道,內(nèi)核空間的地址分成三個部分,ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM,其中前面兩部分是用來和物理地址進(jìn)行一一映射的,而ZONE_HIGHMEM通過臨時借用以及映射的方法管理高于1G的內(nèi)存,vm_struct所使用的內(nèi)核虛擬地址就是ZONE_HIGHMEM部分地址。
http://blog4jimmy.com/2018/01/348.html
總結(jié)
以上是生活随笔為你收集整理的(整理)用户空间_内核空间以及内存映射的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android SystemServic
- 下一篇: 区块链技术:智能合约入门