1. 程式人生 > >linux記憶體佈局及頁面對映

linux記憶體佈局及頁面對映

       在Linux系統中,以32bit x86系統來說,程序的4GB記憶體空間(虛擬地址空間)被劃分成為兩個部分

------使用者空間和核心空間,大小分別為0-3G3-4G

       使用者程序通常情況下,只能訪問使用者空間的虛擬地址,不能訪問到核心空間。

       每個程序的使用者空間存放使用者的程式和程式碼(堆疊,資料區,程式碼區等),因為是虛擬地址,所以每個程序的使用者空間是完全獨立的,互不影響。使用者程序有自己的程序頁表。

      核心空間是核心負責對映實體地址的(所有的程序共享一份核心空間的對映???這個還不太確定),核心空間有自己對應的頁表,它與使用者空間是獨立的。

1.我們先來看一下使用者空間的記憶體佈局(即程序在記憶體的佈局--C執行時庫對記憶體的分配和管理)(下圖中0--3G的範 圍):

    從上圖我們可以看到,使用者程序的程式碼區一般從虛擬地址空間的0x08048000開始,這是為了便於檢查空指標。程式碼區之上便是資料區,未初始化資料區,堆區,棧區,以及引數、全域性環境變數。使用者空間記憶體佈局可以分為以下幾個方面(使用者程式中所申請分配的都是使用者空間的虛擬地址):

   程式碼段(.text/ code segment):這裡存放程式執行程式碼(cpu要執行的指令)的一塊記憶體區域。這部分割槽域在程式執行前就確定了,通常情況下是隻讀的(防止程式由於錯誤而修改自身指令)。某些結構也允許程式碼段可寫,即可以修改程式。程式碼段是可以共享的,相同的程式碼在記憶體中只有一份拷貝。除了程式碼外,這個區域裡面也可能包含一些只讀的常數變數,如字串常量  char *p="12345" ,這裡“12345”就存在程式碼段裡面。


    初始化資料段(.data segment):這裡存放的是程式中需要明確賦初始值的變數,如已經初始化的全域性變數。資料段屬於靜態記憶體分配。包括static變數。


    未初始化資料段(.bss):存放未經初始化的全域性變數。核心在執行該程式前,將其初始化為0或者null。BBS(block started by symbol), 屬於靜態記憶體分配。

   堆(Heap):存放程式中進行動態記憶體申請,例如經常用到的malloc,new系列函式就是從這個段中申請記憶體。堆的大小不固定,可以動態增加(malloc)和縮減(free)

    棧(Stack):又稱堆疊,函式中的區域性變數以及在函式呼叫過程中產生的臨時變數都儲存在此段中。(在函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且待到呼叫結束後,函式的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來儲存/恢復呼叫現場。從這個意義上講,我們可以把堆疊看成一個寄存、交換臨時資料的記憶體區。 )

注:

1. 程式碼段(.text)和初始化資料段(.data) 都位於程式的可執行檔案中,核心在呼叫exec函式啟動該程式時從源程式檔案中讀入。
2. 棧是自頂向下擴充套件,棧是有界的(系統為 stack 區域保留了 128M 記憶體地址空間)。 堆是自底向上擴充套件,mmap對映區自頂向下擴充套件,mmap 對映區域和堆相對擴充套件,直至耗盡虛擬地址空間中的剩餘區域,這種結構便於 C 執行時庫使用 mmap 對映區域和堆進行記憶體分配。

3.棧和 mmap 對映區域並不是從一個固定地址開始,並且每次的值都不一樣,這是程式在啟動時隨機改變這些值的設定,使得使用緩衝區溢位進行攻擊更加困難。當然也可以讓程式的棧和 mmap 對映區域從一個固定位置開始,只需要設定全域性變數 randomize_v a_space 值為 0 ,這個變數預設值為 1 。使用者可以通過設定/proc/sys/kernel/randomize_va_space 來停用該特性,也可以用如下命令:

sudo sysctl -w kernel.randomize_va_space=0

4.bss段(未手動初始化的資料)並不給該段的資料分配空間,只是記錄資料所需空間的大小。bss中未經初始化 的變數由exec初始化為0.5.data(已手動初始化的資料)段則為資料分配空間,資料儲存在目標檔案中。

6. 程式碼段的大小在聯結器連結之前,就得到,資料段包含經過初始化的全域性變數以及它們的值BSS段的大小從可執行檔案中得到,然後連結器得到這個大小的記憶體塊,緊跟在資料段後面。當這個記憶體區進入程式的地址空間後全部清零。包含資料段和BSS段的整個區段此時通常稱為資料區

2.在程序空間的3-4G之間的核心空間中,從低地址到高地址依次為:實體記憶體對映區隔離帶—vmalloc虛擬記憶體分配區隔離帶高階記憶體對映區專用頁面對映區保留區

 

對照左右兩個圖(右邊的圖對於3G--3G+896範圍分的更加詳細一些),主要有以下幾個區域(從上到下)

(1)高階128M由3部分組成(3G+896M--4G): ---這段空間需要通過頁錶轉換才能與實體地址空間對映

  • 高階記憶體臨時核心對映區(temporary kernel mapping)
  • 高階記憶體永久核心對映區(persistent kernel mapping)
  • vmalloc用來分配實體地址非連續的記憶體空間(vmalloc area)

(2)系統實體記憶體對映區(從下到上3G--3G+896):----這段空間可以跟實體地址空間896M以下進行直接對映

  • 3G--3G+16M(低端16M區域):用於DMA操作
  • kernel image
  • mem_map
  • 其他記憶體區域

  【核心空間記憶體動態申請】主要包括三個函式:kmalloc(), __get_free_pages, vmalloc

     kmalloc(),__get_free_pages申請的記憶體位於實體地址對映區(3G--3G+896),且在物理上也是連續的,它們與真實的實體地址只有一個固定偏

    移(可以直接對映,因此存在較簡單的轉換關係。而vmalloc申請的記憶體位於vmalloc虛擬記憶體分配區(這些區都是以線性地址為度量),它在

    虛擬記憶體空間給出一塊連續的記憶體區,實質上,這片連續的虛擬記憶體在實體記憶體中並不一定連續,需要通過頁錶轉換

   因為vmalloc申請的在虛擬記憶體空間連續的記憶體區在實體記憶體中並不一定連續,可以想象為了完成vmalloc,新的頁表需要被建立,因此,呼叫

    vmalloc配少量記憶體是不妥的。一般來講,kmalloc用來分配小於128K的記憶體,而更大的記憶體塊需要用vmalloc來實現

  【虛擬地址與實體地址關係】

   對於核心實體記憶體對映區的虛擬記憶體(用kmalloc(), __get_free_pages申請的),使用virt_to_phys()phys_to_virt()來實現實體地址和核心虛擬

   地址之間的互相轉換。它實際上,僅僅做了3G的地址移位。

3. 下面我們來看下x86實際的實體地址空間佈局:

   

linux中頁為單位來管理記憶體,核心將所有的實體地址劃分為2^12個頁面(Linux系統在初始化時,會根據實際的實體記憶體的大小,為每個物理頁面建立一個page物件,所有的page物件構成一個mem_map陣列),所有物理頁面從圖上可以看到劃分到3類記憶體管理區中,ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。 

  • ZONE_DMA的範圍是0~16M:

         0-640K 被PCI裝置的I/O記憶體對映佔據,它們的大小和佈局由PCI規範所決定。

         640K~1M這段地址空間被BIOS和VGA介面卡所佔據。

         由於這兩段地址空間的存在,導致相應的RAM空間不能被CPU所定址(當CPU訪問該段地址時,北橋會自動將目的實體地址“路由”到相應的I/O裝置上,不會發送給RAM),從而形成RAM空洞。

 在系統初始化階段,核心首先在真實模式下建立一個實體地址對映來指定哪些實體地址範圍對核心可用而哪些不可用(主要是根據對映硬體裝置I/O的共享記憶體,或者根據相應的頁框含有的BIOS資料)。

   記憶體某些部分永久地分配給BOIS或核心,用來存放BIOS資訊、核心程式碼以及靜態核心資料結構。所以核心將下列頁框記為保留:
• 在不可用的實體地址範圍內的頁框,一般用來存放BIOS資訊。
• 含有核心程式碼和已初始化的資料結構的頁框。
  標記為保留頁框中的頁,絕不能被動態分配或交換到磁碟上。

  一般來說,Linux核心安裝在RAM中從實體地址0x00100000開始的地方,也就是說,從第二個MB開始。所需頁框總數依賴於核心的配置方案:典型的配置所得到的核心可以完全被安裝在小於3MB的RAM中。為什麼不從第一個M開始?因為第0個頁給了BIOS使用,存放加電自檢(Power-On Self-Test,POST)期間檢查到的硬體配置。因此,很多膝上型電腦的BIOS甚至在系統初始化後還將資料寫到該頁框。實體地址從0x000a0000 到 0x000fffff的範圍通常留給BIOS例程,雖然前1M裡BIOS並沒有使用完,但是為了避免把核心裝入一組不連續的頁框裡,影響效能,Linux便跳過第1MB的RAM,之間從第2個MB開始載入。

     1M-16M 該區域的物理頁面專門供I/O裝置的DMA使用。之所以需要單獨管理DMA的物理頁面,是因為DMA使用實體地址訪問

     記憶體,不經過MMU,並且需要連續的緩衝區,所以為了能夠提供物理上連續的緩衝區,必須從實體地址空間專門劃分一段區

     域用於DMA。

  • ZONE_NORMAL的範圍是16M~896M,該區域的物理頁面是核心能夠直接使用的(可以到核心3G--3G+896M直接對映)。
  • ZONE_HIGHMEM的範圍是896M~結束,該區域即為高階記憶體,核心不能直接使用。

4. 核心空間和物理空間的對映:

  

   1. 由於ZONE_NORMAL和核心線性空間存在直接對映關係,所以核心會將頻繁使用的資料如kernel程式碼、GDT、IDT、PGD、mem_map陣列等放在ZONE_NORMAL裡。而將使用者資料、頁表(PT)等不常用資料放在ZONE_ HIGHMEM裡,只在要訪問這些資料時才建立對映關係(kmap())。比如,當核心要訪問I/O裝置儲存空間時,就使用ioremap()將位於實體地址高階的mmio區記憶體對映到核心空間的vmalloc area中,在使用完之後便斷開對映關係。 

   2.由於開啟了分頁機制,核心想要訪問實體地址空間的話,必須先建立對映關係,然後通過虛擬地址來訪問。為了能夠訪問所有的實體地址空間,就要將全部實體地址空間對映到1G的核心線性空間中,這顯然不可能。於是,核心將0~896M的實體地址空間一對一對映到自己的線性地址空間中,這樣它便可以隨時訪問ZONE_DMA和ZONE_NORMAL裡的物理頁面;此時核心剩下的128M線性地址空間不足以完全對映所有的ZONE_HIGHMEM,Linux採取了動態對映的方法,即按需的將ZONE_HIGHMEM裡的物理頁面對映到kernel space的最後128M線性地址空間裡,使用完之後釋放對映關係,以供其它物理頁面對映。雖然這樣存在效率的問題,但是核心畢竟可以正常的訪問所有的實體地址空間了。

5. 一般使用者空間要通過核心空間,最終對映到實體地址空間(????),使用者空間與物理空間也可能存在直接的對映:

  當RAM足夠多時,核心會將使用者資料儲存在ZONE_ HIGHMEM,從而為核心騰出記憶體空間。

 接下來需要學習:

  虛擬地址與實體地址的轉換

  裝置io地址到核心空間的對映,使用者態訪問裝置地址

  虛擬記憶體區域vma ---可執行程式與虛擬地址空間的對映(高階記憶體對映--kmap)

   為什麼使用者空間到實體地址空間,要經過核心空間?

  頁面管理--記憶體管理

  IO埠 IO記憶體 IO對映方式 記憶體對映方式    ---高老師的關於裝置io地址空間 

相關推薦

linux記憶體佈局頁面對映

       在Linux系統中,以32bit x86系統來說,程序的4GB記憶體空間(虛擬地址空間)被劃分成為兩個部分 ------使用者空間和核心空間,大小分別為0-3G,3-4G。        使用者程序通常情況下,只能訪問使用者空間的虛擬地址,不能訪問到核心空間。

linux記憶體管理解析----linux物理,線性記憶體佈局頁表的初始化

早就想搞一下記憶體問題了!這次正趁著搞bigmemory核心,可以寫一篇文章了。本文旨在記錄,不包含細節,細節的話,google,百度均可,很多人已經寫了不少了。我只是按照自己的理解記錄一下記憶體的點點滴滴而已,沒有一家之言,不討論,不較真。 1.最簡單的記憶體使用 最簡單的模型是馮.諾依曼提出的原始模型,

C++/C程式記憶體佈局函式棧結構

一:系統的程式佈局圖: 二、詳解: 程式空間: 包括.txt程式碼段,.data資料段, .bss段,堆段,棧段。程式的地址從低往高。堆空間增長方向從低地址往高地址增長。 棧空間從高地址往低地址方向增長。從左往右方向為單位元組增長方向。

Linux記憶體管理之反向對映RMAP

1. Linux反向對映有三個常用資料結構,可以簡稱AV, VMA,AVC struct anon_vma {     struct anon_vma *root;//指向紅黑樹最頂層AV,可以理解為祖宗程序的AV     unsign

JVM層次理解物件建立、記憶體佈局訪問定位

物件的建立 物件的建立,當虛擬機器接收到new指令,會去檢查這個指令的引數是否能在常量池中定位到一個類的符號引用,並且檢查該引用是否已經被載入、解析和初始化過,如果沒有則必須經歷該過程。當類載入檢查通過後,物件建立所需的堆記憶體空間在類載入期間就已經完全確定,接下來在堆中以

Linux記憶體檢視解釋 free

一、命令 1 2 3 4 5 [[email protected] ~]# free -m              total       used       free     shared

系統技術非業餘研究 » Linux下試驗大頁面對映(MAP_HUGETLB)

Linux對大頁面記憶體的引入對減少TLB的失效效果不錯,特別是記憶體大而密集型的程式,比如說在資料庫中的使用。innodb引擎就支援大頁面記憶體,具體使用可參見 這裡。 大頁面更詳細的資料可以參考: Documentation/vm/hugetlbpage.txt 過去使用大頁面記憶體主要透過h

SharePoint 2013 入門教程之建立頁面佈局頁面

  在SharePoint的使用過程中,頁面佈局和頁面時很重要的兩個概念,主要用於資料個性化展示,下面,我們簡單介紹一下SharePoint的頁面佈局和頁面的個性化。 一、 SharePoint頁面模型概述   如下圖,是SharePoint頁面模型圖,我們可以看出母版頁、

理解Java物件:要從記憶體佈局底層機制說起,話說….

前言 大家好,又見面了,今天是JVM專題的第二篇文章,在上一篇文章中我們說了Java的類和物件在JVM中的儲存方式,並使用HSDB進行佐證,沒有看過上一篇文章的小夥伴可以點這裡:《類和物件在JVM中是如何儲存的,竟然有一半人回答不上來!》 這篇文章主要會對Java物件進行詳細分析,基於上一篇文章,對Java物

微信小程式(看文件寫例項四)微信小程式課堂寶APP實現簽到子頁面佈局課程視訊播放頁面

一、簽到子頁面佈局 子頁面主要是一個簽到按鈕,然後下方是簽到記錄列表。 1、簽到按鈕 佈局程式碼: <button class='sign-button' bindtap='sign'>簽到</button>

Linux程序地址空間 程序記憶體佈局

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

記憶體管理九 linux記憶體頁面回收

一、概序:   在記憶體緊張時,核心會將很少使用的記憶體換出到交換分割槽,以便釋放出實體記憶體,此種機制成為“頁交換”, 也統稱為頁面回收,頁面回收涉及到LRU連結串列、記憶體回收演算法、Kswapd核心執行緒等知識,下面會做相關介紹。 二、LRU連結串列: 1、LRU連結串列:

Linux記憶體描述之記憶體頁面page--Linux記憶體管理(四)

1 Linux如何描述實體記憶體 Linux把實體記憶體劃分為三個層次來管理 層次 描述 儲存節點(Node) CPU被劃分為多個節點(node), 記憶體則被分簇, 每個CPU對應一個本地實體記憶體, 即一個CPU-node對應

Linux程序地址空間 && 程序記憶體佈局

轉載自:https://blog.csdn.net/yusiguyuan/article/details/45155035   一 程序空間分佈概述       對於一個程序,其空間分佈如下圖所示:      

jQuery 實現瀑布流佈局下滑載入頁面

蘑菇街商品的資訊,一個接一個,銜接緊密,根據瀏覽器大小有幾列,元素等寬不等高,向下滑動載入 就是常見的瀑布流佈局, 實現瀑布流佈局有三種方式,JavaScript原生,jQuery,和CSS3 column方法。 我寫了JavaScript 和jQuery兩種。 jQu

Linux ------- 記憶體對映

一、記憶體對映的原理     記憶體對映,簡而言之就是將使用者空間的一段記憶體區域對映到核心空間,對映成功後,使用者對這段記憶體區域的修改可以直接反映到核心空間,同樣,核心空間對這段區域的修改也直接反映使用者空間。那麼對於核心空間<---->使用者空間兩者之

CSS頁面佈局排版

一、CSS盒子模型   CSS將每個元素看成是一個矩形盒子,佔據一定空間。 盒子模型兩方面理解:1.獨立的盒子內部結構;2.多個盒子之間的香菇關係。   1.盒子模型概念 獨立盒子模型由:內容、border、padding、margin四部分組成。詳見之前的文章內容。 盒子的實際寬(高)=

linux記憶體對映mmap原理分析

一直都對記憶體對映檔案這個概念很模糊,不知道它和虛擬記憶體有什麼區別,而且對映這個詞也很讓人迷茫,今天終於搞清楚了。。。下面,我先解釋一下我對對映這個詞的理解,再區分一下幾個容易混淆的概念,之後,什麼是記憶體對映就很明朗了。 原理 首先,“對映”這個詞,就和數學課上說

Html5移動端佈局(rem佈局)頁面自適應佈局詳解(轉)

常見的頁面佈局方式有, 靜態佈局  px佈局 流式佈局(Liquid Layout) 主要的劃分區域的尺寸使用百分數(搭配min-*、max-*屬性使用) 自適應佈局(Adaptive Layout) 即建立多個靜態佈局,每個靜態佈局對應一個螢幕解析度範圍 響應式

Linux使用者程序記憶體分配二級頁表PTE的二三事

我們在用偵錯程式看Linux使用者程序程式碼時,發現了一件很有意思的事情,在一段記憶體空間中,有一整頁(4K)都是data abort,如下:第一頁4011c000資料正常... ...4011cfec [0xe28dd014]   add      r13,r13,#0x144011cff0 [0xe8bd