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