linux内存布局及页面映射
在Linux系統(tǒng)中,以32bit x86系統(tǒng)來說,進(jìn)程的4GB內(nèi)存空間(虛擬地址空間)被劃分成為兩個(gè)部分
------用戶空間和內(nèi)核空間,大小分別為0-3G,3-4G。
?????? 用戶進(jìn)程通常情況下,只能訪問用戶空間的虛擬地址,不能訪問到內(nèi)核空間。
?
?????? 每個(gè)進(jìn)程的用戶空間存放用戶的程序和代碼(堆棧,數(shù)據(jù)區(qū),代碼區(qū)等),因?yàn)槭翘摂M地址,所以每個(gè)進(jìn)程的用戶空間是完全獨(dú)立的,互不影響。用戶進(jìn)程有自己的進(jìn)程頁表。
????? 內(nèi)核空間是內(nèi)核負(fù)責(zé)映射物理地址的(所有的進(jìn)程共享一份內(nèi)核空間的映射???這個(gè)還不太確定),內(nèi)核空間有自己對(duì)應(yīng)的頁表,它與用戶空間是獨(dú)立的。
?
1.我們先來看一下用戶空間的內(nèi)存布局(即進(jìn)程在內(nèi)存的布局--C運(yùn)行時(shí)庫對(duì)內(nèi)存的分配和管理)(下圖中0--3G的范 圍):
?
????從上圖我們可以看到,用戶進(jìn)程的代碼區(qū)一般從虛擬地址空間的0x08048000開始,這是為了便于檢查空指針。代碼區(qū)之上便是數(shù)據(jù)區(qū),未初始化數(shù)據(jù)區(qū),堆區(qū),棧區(qū),以及參數(shù)、全局環(huán)境變量。用戶空間內(nèi)存布局可以分為以下幾個(gè)方面(用戶程序中所申請分配的都是用戶空間的虛擬地址):
???代碼段(.text/ code segment):這里存放程序執(zhí)行代碼(cpu要執(zhí)行的指令)的一塊內(nèi)存區(qū)域。這部分區(qū)域在程序運(yùn)行前就確定了,通常情況下是只讀的(防止程序由于錯(cuò)誤而修改自身指令)。某些結(jié)構(gòu)也允許代碼段可寫,即可以修改程序。代碼段是可以共享的,相同的代碼在內(nèi)存中只有一份拷貝。除了代碼外,這個(gè)區(qū)域里面也可能包含一些只讀的常數(shù)變量,如字符串常量? char *p="12345" ,這里“12345”就存在代碼段里面。
????初始化數(shù)據(jù)段(.data segment):這里存放的是程序中需要明確賦初始值的變量,如已經(jīng)初始化的全局變量。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配。包括static變量。
????未初始化數(shù)據(jù)段(.bss):存放未經(jīng)初始化的全局變量。內(nèi)核在執(zhí)行該程序前,將其初始化為0或者null。BBS(block started by symbol), 屬于靜態(tài)內(nèi)存分配。
???堆(Heap):存放程序中進(jìn)行動(dòng)態(tài)內(nèi)存申請,例如經(jīng)常用到的malloc,new系列函數(shù)就是從這個(gè)段中申請內(nèi)存。堆的大小不固定,可以動(dòng)態(tài)增加(malloc)和縮減(free)
????棧(Stack):又稱堆棧,函數(shù)中的局部變量以及在函數(shù)調(diào)用過程中產(chǎn)生的臨時(shí)變量都保存在此段中。(在函數(shù)被調(diào)用時(shí),其參數(shù)也會(huì)被壓入發(fā)起調(diào)用的進(jìn)程棧中,并且待到調(diào)用結(jié)束后,函數(shù)的返回值也會(huì)被存放回棧中。由于棧的先進(jìn)先出特點(diǎn),所以棧特別方便用來保存/恢復(fù)調(diào)用現(xiàn)場。從這個(gè)意義上講,我們可以把堆棧看成一個(gè)寄存、交換臨時(shí)數(shù)據(jù)的內(nèi)存區(qū)。?)
?
注:
1. 代碼段(.text)和初始化數(shù)據(jù)段(.data) 都位于程序的可執(zhí)行文件中,內(nèi)核在調(diào)用exec函數(shù)啟動(dòng)該程序時(shí)從源程序文件中讀入。
2. 棧是自頂向下擴(kuò)展,棧是有界的(系統(tǒng)為?stack?區(qū)域保留了?128M?內(nèi)存地址空間)。 堆是自底向上擴(kuò)展,mmap映射區(qū)自頂向下擴(kuò)展,mmap?映射區(qū)域和堆相對(duì)擴(kuò)展,直至耗盡虛擬地址空間中的剩余區(qū)域,這種結(jié)構(gòu)便于?C?運(yùn)行時(shí)庫使用?mmap?映射區(qū)域和堆進(jìn)行內(nèi)存分配。3.棧和?mmap?映射區(qū)域并不是從一個(gè)固定地址開始,并且每次的值都不一樣,這是程序在啟動(dòng)時(shí)隨機(jī)改變這些值的設(shè)置,使得使用緩沖區(qū)溢出進(jìn)行攻擊更加困難。當(dāng)然也可以讓程序的棧和?mmap?映射區(qū)域從一個(gè)固定位置開始,只需要設(shè)置全局變量?randomize_v?a_space?值為?0?,這個(gè)變量默認(rèn)值為?1?。用戶可以通過設(shè)置/proc/sys/kernel/randomize_va_space?來停用該特性,也可以用如下命令:
???????sudo sysctl -w kernel.randomize_va_space=0
4.bss段(未手動(dòng)初始化的數(shù)據(jù))并不給該段的數(shù)據(jù)分配空間,只是記錄數(shù)據(jù)所需空間的大小。bss中未經(jīng)初始化 的變量由exec初始化為0.
5.data(已手動(dòng)初始化的數(shù)據(jù))段則為數(shù)據(jù)分配空間,數(shù)據(jù)保存在目標(biāo)文件中。6. 代碼段的大小在連接器鏈接之前,就得到,數(shù)據(jù)段包含經(jīng)過初始化的全局變量以及它們的值,BSS段的大小從可執(zhí)行文件中得到?,然后鏈接器得到這個(gè)大小的內(nèi)存塊,緊跟在數(shù)據(jù)段后面。當(dāng)這個(gè)內(nèi)存區(qū)進(jìn)入程序的地址空間后全部清零。包含數(shù)據(jù)段和BSS段的整個(gè)區(qū)段此時(shí)通常稱為數(shù)據(jù)區(qū)
?
2.在進(jìn)程空間的3-4G之間的內(nèi)核空間中,從低地址到高地址依次為:物理內(nèi)存映射區(qū)—隔離帶—vmalloc虛擬內(nèi)存分配區(qū)—隔離帶—高端內(nèi)存映射區(qū)—專用頁面映射區(qū)—保留區(qū)
?
對(duì)照左右兩個(gè)圖(右邊的圖對(duì)于3G--3G+896范圍分的更加詳細(xì)一些),主要有以下幾個(gè)區(qū)域(從上到下)
(1)高端128M由3部分組成(3G+896M--4G): ---這段空間需要通過頁表轉(zhuǎn)換才能與物理地址空間映射
- 高端內(nèi)存臨時(shí)內(nèi)核映射區(qū)(temporary kernel mapping)
- 高端內(nèi)存永久內(nèi)核映射區(qū)(persistent kernel mapping)
- vmalloc用來分配物理地址非連續(xù)的內(nèi)存空間(vmalloc area)?
(2)系統(tǒng)物理內(nèi)存映射區(qū)(從下到上3G--3G+896):----這段空間可以跟物理地址空間896M以下進(jìn)行直接映射
- 3G--3G+16M(低端16M區(qū)域):用于DMA操作
- kernel image
- mem_map
- 其他內(nèi)存區(qū)域
?
??【內(nèi)核空間內(nèi)存動(dòng)態(tài)申請】主要包括三個(gè)函數(shù):kmalloc(), __get_free_pages, vmalloc。 ???? kmalloc(),__get_free_pages申請的內(nèi)存位于物理地址映射區(qū)(3G--3G+896),且在物理上也是連續(xù)的,它們與真實(shí)的物理地址只有一個(gè)固定偏 ??? 移(可以直接映射,因此存在較簡單的轉(zhuǎn)換關(guān)系。而vmalloc申請的內(nèi)存位于vmalloc虛擬內(nèi)存分配區(qū)(這些區(qū)都是以線性地址為度量),它在 ??? 虛擬內(nèi)存空間給出一塊連續(xù)的內(nèi)存區(qū),實(shí)質(zhì)上,這片連續(xù)的虛擬內(nèi)存在物理內(nèi)存中并不一定連續(xù),需要通過頁表轉(zhuǎn)換。 ???因?yàn)関malloc申請的在虛擬內(nèi)存空間連續(xù)的內(nèi)存區(qū)在物理內(nèi)存中并不一定連續(xù),可以想象為了完成vmalloc,新的頁表需要被建立,因此,調(diào)用 ??? vmalloc來分配少量內(nèi)存是不妥的。一般來講,kmalloc用來分配小于128K的內(nèi)存,而更大的內(nèi)存塊需要用vmalloc來實(shí)現(xiàn) ? ? 【虛擬地址與物理地址關(guān)系】 ?? 對(duì)于內(nèi)核物理內(nèi)存映射區(qū)的虛擬內(nèi)存(用kmalloc(), __get_free_pages申請的),使用virt_to_phys()和phys_to_virt()來實(shí)現(xiàn)物理地址和內(nèi)核虛擬 ?? 地址之間的互相轉(zhuǎn)換。它實(shí)際上,僅僅做了3G的地址移位。 ? |
?
?
3. 下面我們來看下x86實(shí)際的物理地址空間布局:
???
?
linux中頁為單位來管理內(nèi)存,內(nèi)核將所有的物理地址劃分為2^12個(gè)頁面(Linux系統(tǒng)在初始化時(shí),會(huì)根據(jù)實(shí)際的物理內(nèi)存的大小,為每個(gè)物理頁面創(chuàng)建一個(gè)page對(duì)象,所有的page對(duì)象構(gòu)成一個(gè)mem_map數(shù)組),所有物理頁面從圖上可以看到劃分到3類內(nèi)存管理區(qū)中,ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。?
- ZONE_DMA的范圍是0~16M:
??????? ?0-640K 被PCI設(shè)備的I/O內(nèi)存映射占據(jù),它們的大小和布局由PCI規(guī)范所決定。
???????? 640K~1M這段地址空間被BIOS和VGA適配器所占據(jù)。
?
???????? 由于這兩段地址空間的存在,導(dǎo)致相應(yīng)的RAM空間不能被CPU所尋址(當(dāng)CPU訪問該段地址時(shí),北橋會(huì)自動(dòng)將目的物理地址“路由”到相應(yīng)的I/O設(shè)備上,不會(huì)發(fā)送給RAM),從而形成RAM空洞。
?
|
???? 1M-16M 該區(qū)域的物理頁面專門供I/O設(shè)備的DMA使用。之所以需要單獨(dú)管理DMA的物理頁面,是因?yàn)镈MA使用物理地址訪問
???? 內(nèi)存,不經(jīng)過MMU,并且需要連續(xù)的緩沖區(qū),所以為了能夠提供物理上連續(xù)的緩沖區(qū),必須從物理地址空間專門劃分一段區(qū)
???? 域用于DMA。?
- ZONE_NORMAL的范圍是16M~896M,該區(qū)域的物理頁面是內(nèi)核能夠直接使用的(可以到內(nèi)核3G--3G+896M直接映射)。?
- ZONE_HIGHMEM的范圍是896M~結(jié)束,該區(qū)域即為高端內(nèi)存,內(nèi)核不能直接使用。
- ?
?
4. 內(nèi)核空間和物理空間的映射:
???
?
?? 1. 由于ZONE_NORMAL和內(nèi)核線性空間存在直接映射關(guān)系,所以內(nèi)核會(huì)將頻繁使用的數(shù)據(jù)如kernel代碼、GDT、IDT、PGD、mem_map數(shù)組等放在ZONE_NORMAL里。而將用戶數(shù)據(jù)、頁表(PT)等不常用數(shù)據(jù)放在ZONE_ HIGHMEM里,只在要訪問這些數(shù)據(jù)時(shí)才建立映射關(guān)系(kmap())。比如,當(dāng)內(nèi)核要訪問I/O設(shè)備存儲(chǔ)空間時(shí),就使用ioremap()將位于物理地址高端的mmio區(qū)內(nèi)存映射到內(nèi)核空間的vmalloc area中,在使用完之后便斷開映射關(guān)系。?
?? 2.由于開啟了分頁機(jī)制,內(nèi)核想要訪問物理地址空間的話,必須先建立映射關(guān)系,然后通過虛擬地址來訪問。為了能夠訪問所有的物理地址空間,就要將全部物理地址空間映射到1G的內(nèi)核線性空間中,這顯然不可能。于是,內(nèi)核將0~896M的物理地址空間一對(duì)一映射到自己的線性地址空間中,這樣它便可以隨時(shí)訪問ZONE_DMA和ZONE_NORMAL里的物理頁面;此時(shí)內(nèi)核剩下的128M線性地址空間不足以完全映射所有的ZONE_HIGHMEM,Linux采取了動(dòng)態(tài)映射的方法,即按需的將ZONE_HIGHMEM里的物理頁面映射到kernel?space的最后128M線性地址空間里,使用完之后釋放映射關(guān)系,以供其它物理頁面映射。雖然這樣存在效率的問題,但是內(nèi)核畢竟可以正常的訪問所有的物理地址空間了。
?
5.?一般用戶空間要通過內(nèi)核空間,最終映射到物理地址空間(????),用戶空間與物理空間也可能存在直接的映射:
? 當(dāng)RAM足夠多時(shí),內(nèi)核會(huì)將用戶數(shù)據(jù)保存在ZONE_ HIGHMEM,從而為內(nèi)核騰出內(nèi)存空間。
https://blog.csdn.net/sinat_16790541/article/details/42244757
總結(jié)
以上是生活随笔為你收集整理的linux内存布局及页面映射的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电影《拯救大兵瑞恩》的内容是?类似《拯救
- 下一篇: Linux 的内存管理工具和调优参数