今天來帶你們研究一下Linux
內存管理。對於精通 CURD
的業務同窗,內存管理好像離咱們很遠,但這個知識點雖然冷門(估計不少人學完根本就沒機會用上)但絕對是基礎中的基礎,這就像武俠中的內功修煉,學完以後看不到立竿見影的效果,但對你往後的開發工做是大有裨益的,由於你站的更高了。linux
文中全部示例圖都是我親手畫的,畫圖比碼字還費時間,可是看圖理解比文字更直觀,須要高清示例圖片的同窗,文末有獲取方式自取。程序員
再功利點的說,面試的時候不經意間透露你懂這方面知識,而且能說出個一二三來,也許能讓面試官對你更有興趣,離升職加薪,走上人生巔峯又近了一步。面試
前提約定:本文討論技術內容前提,操做系統環境都是 x86
架構的 32 位 Linux
系統。redis
即便是現代操做系統中,內存依然是計算機中很寶貴的資源,看看你電腦幾個T固態硬盤,再看看內存大小就知道了。爲了充分利用和管理系統內存資源,Linux採用虛擬內存管理技術,利用虛擬內存技術讓每一個進程都有4GB
互不干涉的虛擬地址空間。shell
進程初始化分配和操做的都是基於這個「虛擬地址」,只有當進程須要實際訪問內存資源的時候纔會創建虛擬地址和物理地址的映射,調入物理內存頁。編程
打個不是很恰當的比方。這個原理其實和如今的某某網盤同樣,假如你的網盤空間是1TB
,真覺得就一口氣給了你這麼大空間嗎?那仍是太年輕,都是在你往裏面放東西的時候纔給你分配空間,你放多少就分多少實際空間給你,但你和你朋友看起來就像你們都擁有1TB
空間同樣。後端
4GB
的進程虛擬地址空間被分紅兩部分:「用戶空間」和「內核空間」微信
上面章節咱們已經知道不論是用戶空間仍是內核空間,使用的地址都是虛擬地址,當需進程要實際訪問內存的時候,會由內核的「請求分頁機制」產生「缺頁異常」調入物理內存頁。數據結構
把虛擬地址轉換成內存的物理地址,這中間涉及利用MMU
內存管理單元(Memory Management Unit ) 對虛擬地址分段和分頁(段頁式)地址轉換,關於分段和分頁的具體流程,這裏再也不贅述,能夠參考任何一本計算機組成原理教材描述。架構
Linux
內核會將物理內存分爲3個管理區,分別是:
DMA
內存區域。包含0MB~16MB之間的內存頁框,能夠由老式基於ISA
的設備經過DMA
使用,直接映射到內核的地址空間。
普通內存區域。包含16MB~896MB之間的內存頁框,常規頁框,直接映射到內核的地址空間。
高端內存區域。包含896MB以上的內存頁框,不進行直接映射,能夠經過永久映射和臨時映射進行這部份內存頁框的訪問。
用戶進程能訪問的是「用戶空間」,每一個進程都有本身獨立的用戶空間,虛擬地址範圍從從 0x00000000
至 0xBFFFFFFF
總容量3G 。
用戶進程一般只能訪問用戶空間的虛擬地址,只有在執行內陷操做或系統調用時才能訪問內核空間。
進程(執行的程序)佔用的用戶空間按照「 訪問屬性一致的地址空間存放在一塊兒 」的原則,劃分紅 5
個不一樣的內存區域。 訪問屬性指的是「可讀、可寫、可執行等 。
代碼段
代碼段是用來存放可執行文件的操做指令,可執行程序在內存中的鏡像。代碼段須要防止在運行時被非法修改,因此只准許讀取操做,它是不可寫的。
數據段
數據段用來存放可執行文件中已初始化全局變量,換句話說就是存放程序靜態分配的變量和全局變量。
BSS段
BSS
段包含了程序中未初始化的全局變量,在內存中 bss
段所有置零。
堆 heap
堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)
棧 stack
棧是用戶存放程序臨時建立的局部變量,也就是函數中定義的變量(但不包括 static
聲明的變量,static意味着在數據段中存放變量)。除此之外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,而且待到調用結束後,函數的返回值也會被存放回棧中。因爲棧的先進先出特色,因此棧特別方便用來保存/恢復調用現場。從這個意義上講,咱們能夠把堆棧當作一個寄存、交換臨時數據的內存區。
上述幾種內存區域中數據段、BSS
段、堆一般是被連續存儲在內存中,在位置上是連續的,而代碼段和棧每每會被獨立存放。堆和棧兩個區域在 i386
體系結構中棧向下擴展、堆向上擴展,相對而生。
你也能夠再linux下用size
命令查看編譯後程序的各個內存區域大小:
[lemon ~]# size /usr/local/sbin/sshd text data bss dec hex filename 1924532 12412 426896 2363840 2411c0 /usr/local/sbin/sshd
在 x86 32
位系統裏,Linux 內核地址空間是指虛擬地址從 0xC0000000
開始到 0xFFFFFFFF
爲止的高端內存地址空間,總計 1G
的容量, 包括了內核鏡像、物理頁面表、驅動程序等運行在內核空間 。
直接映射區 Direct Memory Region
:從內核空間起始地址開始,最大896M
的內核空間地址區間,爲直接內存映射區。
直接映射區的896MB的「線性地址」直接與「物理地址」的前896MB
進行映射,也就是說線性地址和分配的物理地址都是連續的。內核地址空間的線性地址0xC0000001
所對應的物理地址爲0x00000001
,它們之間相差一個偏移量PAGE_OFFSET = 0xC0000000
該區域的線性地址和物理地址存在線性轉換關係「線性地址 = PAGE_OFFSET
+ 物理地址」也能夠用 virt_to_phys()
函數將內核虛擬空間中的線性地址轉化爲物理地址。
內核空間線性地址從 896M 到 1G 的區間,容量 128MB 的地址區間是高端內存線性地址空間,爲何叫高端內存線性地址空間?下面給你解釋一下:
前面已經說過,內核空間的總大小 1GB,從內核空間起始地址開始的 896MB 的線性地址能夠直接映射到物理地址大小爲 896MB 的地址區間。退一萬步,即便內核空間的1GB線性地址都映射到物理地址,那也最多隻能尋址 1GB 大小的物理內存地址範圍。
請問你如今你家的內存條多大?快醒醒都 0202 年了,通常 PC 的內存都大於 1GB 了吧!
因此,內核空間拿出了最後的 128M 地址區間,劃分紅下面三個高端內存映射區,以達到對整個物理地址範圍的尋址。而在 64 位的系統上就不存在這樣的問題了,由於可用的線性地址空間遠大於可安裝的內存。
vmalloc Region
該區域由內核函數vmalloc
來分配,特色是:線性空間連續,可是對應的物理地址空間不必定連續。 vmalloc
分配的線性地址所對應的物理頁可能處於低端內存,也可能處於高端內存。
Persistent Kernel Mapping Region
該區域可訪問高端內存。訪問方法是使用 alloc_page (_GFP_HIGHMEM)
分配高端內存頁或者使用kmap
函數將分配到的高端內存映射到該區域。
Fixing kernel Mapping Region
該區域和 4G 的頂端只有 4k 的隔離帶,其每一個地址項都服務於特定的用途,如 ACPI_BASE
等。
上面講的有點多,先彆着急進入下一節,在這以前咱們再來回顧一下上面所講的內容。若是認真看完上面的章節,我這裏再畫了一張圖,如今你的腦海中應該有這樣一個內存管理的全局圖。
要讓內核管理系統中的虛擬內存,必然要從中抽象出內存管理數據結構,內存管理操做如「分配、釋放等」都基於這些數據結構操做,這裏列舉兩個管理虛擬內存區域的數據結構。
在前面「進程與內存」章節咱們提到,Linux進程能夠劃分爲 5 個不一樣的內存區域,分別是:代碼段、數據段、BSS
、堆、棧,內核管理這些區域的方式是,將這些內存區域抽象成vm_area_struct
的內存管理對象。
vm_area_struct
是描述進程地址空間的基本管理單元,一個進程每每須要多個vm_area_struct
來描述它的用戶空間虛擬地址,須要使用「鏈表」和「紅黑樹」來組織各個vm_area_struct
。
鏈表用於須要遍歷所有節點的時候用,而紅黑樹適用於在地址空間中定位特定內存區域。內核爲了內存區域上的各類不一樣操做都能得到高性能,因此同時使用了這兩種數據結構。
用戶空間進程的地址管理模型:
在內核空間章節咱們提到過「動態內存映射區」,該區域由內核函數vmalloc
來分配,特色是:線性空間連續,可是對應的物理地址空間不必定連續。 vmalloc
分配的線性地址所對應的物理頁可能處於低端內存,也可能處於高端內存。
vmalloc
分配的地址則限於vmalloc_start
與vmalloc_end
之間。每一塊vmalloc
分配的內核虛擬內存都對應一個vm_struct
結構體,不一樣的內核空間虛擬地址之間有4k
大小的防越界空閒區間隔區。與用戶空間的虛擬地址特性同樣,這些虛擬地址與物理內存沒有簡單的映射關係,必須經過內核頁表纔可轉換爲物理地址或物理頁,它們有可能還沒有被映射,當發生缺頁時才真正分配物理頁面。
Linux
內存管理是一個很是複雜的系統,本文所述只是冰山一角,從宏觀角度給你展示內存管理的全貌,但通常來講,這些知識在你和麪試官聊天的時候仍是夠用的,固然我也但願你們可以經過讀書瞭解更深層次的原理。
但願這篇文章能夠做爲一個索引同樣的學習指南,當你想深刻某一點學習的時候能夠在這些章節裏找到切入點,以及這個知識點在內存管理宏觀上的位置。
本文創做過程我也畫了大量的示例圖解,能夠做爲知識索引,我的感受看圖仍是比看文字更清晰明瞭,你能夠在我公衆號「後端技術學堂」後臺回覆「內存管理」獲取這些圖片的高清原圖。
老規矩,感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反覆求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,咱們一塊兒在探討中學習。今天的技術分享就到這裏,咱們下期再見。
很是詳細的 Linux C/C++ 學習路線總結!已拿騰訊offer
面試官又來喊你造飛機了,你來講說看微服務接口怎麼設計?
面試都在問的微服務、服務治理、RPC、下一代微服務框架... 一文帶你完全搞懂!
Linux下「進程」出問題不要慌,資深程序員教你6招搞定!
面試官:你說對MySQL事務很熟?那我問你10個問題
我用大數據分析了一線城市1000多份崗位招聘需求,告訴你如何科學找工做
騰訊後臺開發面試筆試C++知識點參考筆記
還能這麼玩?我用VsCode畫類圖、流程圖、時序圖、狀態圖不要太爽!
面試官:你會幾種redis分佈式鎖?我會三種!
最詳細的我的博客教程搭建教程GithubPages+Jekyll 簡約風格博客
能夠微信搜索公衆號「 後端技術學堂 」回覆「資料」有我給你準備的各類編程學習資料。文章每週持續更新,咱們下期見!