日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Linux內(nèi)存管理 下

 無聲無息 2008-09-22

內(nèi)存管理(下)

物理內(nèi)存管理(頁管理)

Linux內(nèi)核管理物理內(nèi)存是通過分頁機(jī)制實現(xiàn)的,它將整個內(nèi)存劃分成無數(shù)4k(i386體系結(jié)構(gòu)中)大小頁,從而分配和回收內(nèi)存的基本單位便是內(nèi)存頁了。利用分頁管理有助于靈活分配內(nèi)存地址,因為分配時不必要求必須有大塊的連續(xù)內(nèi)存[1],系統(tǒng)可以東一頁、西一頁的湊出所需要的內(nèi)存供進(jìn)程使用。雖然如此,但是實際上系統(tǒng)使用內(nèi)存還是傾向于分配連續(xù)的內(nèi)存塊,因為分配連續(xù)內(nèi)存時,頁表不需要更改,因此能降低TLB的刷新率(頻繁刷新會很大增加訪問速度)。

鑒于上述需求,內(nèi)核分配物理頁為了盡量減少不連續(xù)情況,采用了“伙伴”關(guān)系來管理空閑頁框?;锇殛P(guān)系分配算法大家不應(yīng)陌生——幾乎所有操作系統(tǒng)書都會提到,我們不去詳細(xì)說它了,如果不明白可以參看有關(guān)資料。這里只需要大家明白Linux中空閑頁面的組織和管理利用了伙伴關(guān)系,因此空閑頁面分配時也需要遵循伙伴關(guān)系,最小單位只能是2的冪倍頁面大小。內(nèi)核中分配空閑頁框的基本函數(shù)是get_free_page/get_free_pages,它們或是分配單頁或是分配指定的頁框(24、8…512頁)。

 注意:get_free_page是在內(nèi)核中分配內(nèi)存,不同于malloc在用戶空間中分配,malloc利用堆動態(tài)分配,實際上是調(diào)用brk()系統(tǒng)調(diào)用,該調(diào)用的作用是擴(kuò)大或縮小進(jìn)程堆空間(它會修改進(jìn)程的brk域)。如果現(xiàn)有的內(nèi)存區(qū)域不夠容納堆空間,則會以頁面大小的倍數(shù)位單位,擴(kuò)張或收縮對應(yīng)的內(nèi)存區(qū)域,但brk值并非以頁面大小為倍數(shù)修改,而是按實際請求修改。因此Malloc在用戶空間分配內(nèi)存可以以字節(jié)為單位分配,但內(nèi)核在內(nèi)部仍然會是以頁為單位分配的。

   另外需要提及的是,物理頁在系統(tǒng)中由頁框結(jié)構(gòu)struct paga描述,系統(tǒng)中所有的頁框存儲在數(shù)組mem_map[]中,可以通過該數(shù)組找到系統(tǒng)中的每一頁(空閑或非空閑)。而其中的空閑頁框則可由上述提到的以伙伴關(guān)系組織的空閑頁鏈表(free_area[MAX_ORDER]索引。

 內(nèi)核內(nèi)存使用

Slab

    所 謂尺有所長,寸有所短。以頁為最小單位分配內(nèi)存對于內(nèi)核管理系統(tǒng)物理內(nèi)存來說的確比較方便,但內(nèi)核自身最常使用的內(nèi)存卻往往是很小(遠(yuǎn)遠(yuǎn)小于一頁)的內(nèi)存 塊——比如存放文件描述符、進(jìn)程描述符、虛擬內(nèi)存區(qū)域描述符等行為所需的內(nèi)存都不足一頁。這些用來存放描述符的內(nèi)存相比頁面而言,就好比是面包屑與面包。 一個整頁中可以聚集多個這種這些小塊內(nèi)存;而且這些小塊內(nèi)存塊也和面包屑一樣頻繁地生成/銷毀。

  為了滿足內(nèi)核對這種小內(nèi)存塊的需要,Linux系統(tǒng)采用了一種被稱為slab分配器的技術(shù)。Slab分配器的實現(xiàn)相當(dāng)復(fù)雜,但原理不難,其核心思想就是“存儲池[2]的運用。內(nèi)存片段(小塊內(nèi)存)被看作對象,當(dāng)被使用完后,并不直接釋放而是被緩存到“存儲池”里,留做下次使用,這無疑避免了頻繁創(chuàng)建與銷毀對象所帶來的額外負(fù)載。

Slab技術(shù)不但避免了內(nèi)存內(nèi)部分片(下文將解釋)帶來的不便(引入Slab分配器的主要目的是為了減少對伙伴系統(tǒng)分配算法的調(diào)用次數(shù)——頻繁分配和回收必然會導(dǎo)致內(nèi)存碎片——難以找到大塊連續(xù)的可用內(nèi)存,而且可以很好利用硬件緩存提高訪問速度。

    Slab并非是脫離伙伴關(guān)系而獨立存在的一種內(nèi)存分配方式,slab仍然是建立在頁面基礎(chǔ)之上,換句話說,Slab將頁面(來自于伙伴關(guān)系管理的空閑頁框鏈)撕碎成眾多小內(nèi)存塊以供分配,slab中的對象分配和銷毀使用kmem_cache_allockmem_cache_free。

 

Kmalloc

Slab分配器不僅僅只用來存放內(nèi)核專用的結(jié)構(gòu)體,它還被用來處理內(nèi)核對小塊內(nèi)存的請求。當(dāng)然鑒于Slab分配器的特點,一般來說內(nèi)核程序中對小于一頁的小塊內(nèi)存的求情才通過Slab分配器提供的接口Kmalloc來完成(雖然它可分配32 131072字節(jié)的內(nèi)存)。從內(nèi)核內(nèi)存分配角度講kmalloc可被看成是get_free_pages)的一個有效補充,內(nèi)存分配粒度更靈活了。

有興趣的話可以到/proc/slabinfo中找到內(nèi)核執(zhí)行現(xiàn)場使用的各種slab信息統(tǒng)計,其中你會看到系統(tǒng)中所有slab的使用信息。從信息中可以看到系統(tǒng)中除了專用結(jié)構(gòu)體使用的slab外,還存在大量為Kmalloc而準(zhǔn)備的Slab(其中有些為dma準(zhǔn)備的)。

 

 

內(nèi)核非連續(xù)內(nèi)存分配(Vmalloc

 

伙伴關(guān)系也好、slab技 術(shù)也好,從內(nèi)存管理理論角度而言目的基本是一致的,它們都是為了防止“分片”,不過分片又分為外部分片和內(nèi)部分片之說,所謂內(nèi)部分片是說系統(tǒng)為了滿足一小 段內(nèi)存區(qū)(連續(xù))的需要,不得不分配了一大區(qū)域連續(xù)內(nèi)存給它,從而造成了空間浪費;外部分片是指系統(tǒng)雖有足夠的內(nèi)存,但卻是分散的碎片,無法滿足對大塊“ 連續(xù)內(nèi)存”的需求。無論何種分片都是系統(tǒng)有效利用內(nèi)存的障礙。slab分 配器使得含與一個頁面內(nèi)眾多小塊內(nèi)存可獨立被分配使用,避免了內(nèi)部分片,節(jié)約了空閑內(nèi)存?;锇殛P(guān)系把內(nèi)存塊按大小分組管理,一定程度上減輕了外部分片的危 害,因為頁框分配不在盲目,而是按照大小依次有序進(jìn)行,不過伙伴關(guān)系只是減輕了外部分片,但并未徹底消除。你自己筆畫一下多次分配頁框后,空閑內(nèi)存的剩余 情況吧。

所以避免外部分片的最終思路還是落到了如何利用不連續(xù)的內(nèi)存塊組合成“看起來很大的內(nèi)存塊”——這里的情況很類似于用戶空間分配虛擬內(nèi)存,內(nèi)存邏輯上連續(xù),其實影射到并不一定連續(xù)的物理內(nèi)存上。Linux內(nèi)核借用了這個技術(shù),允許內(nèi)核程序在內(nèi)核地址空間中分配虛擬地址,同樣也利用頁表(內(nèi)核頁表)將虛擬地址影射到分散的內(nèi)存頁上。以此完美地解決了內(nèi)核內(nèi)存使用中的外部分片問題。內(nèi)核提供vmalloc函數(shù)分配內(nèi)核虛擬內(nèi)存,該函數(shù)不同于kmalloc,它可以分配較Kmalloc大得多的內(nèi)存空間(可遠(yuǎn)大于128K,但必須是頁大小的倍數(shù)),但相比Kmalloc來說Vmalloc需要對內(nèi)核虛擬地址進(jìn)行重影射,必須更新內(nèi)核頁表,因此分配效率上要低一些(用空間換時間)

與用戶進(jìn)程相似內(nèi)核也有一個名為init_mmmm_strcut結(jié)構(gòu)來描述內(nèi)核地址空間,其中頁表項pdg=swapper_pg_dir包含了系統(tǒng)內(nèi)核空間(3G-4G)的映射關(guān)系。因此vmalloc分配內(nèi)核虛擬地址必須更新內(nèi)核頁表,而kmallocget_free_page由于分配的連續(xù)內(nèi)存,所以不需要更新內(nèi)核頁表。

 

空閑頁框

APP

內(nèi)存區(qū)域 vm_area_structs

malloc、fork、excute、mmap

brk/do_map

get_free_page(s)

用戶空間

內(nèi)核空間

進(jìn)程虛擬地址空間

 

系統(tǒng)調(diào)用

進(jìn)程頁表

 

請頁異常

內(nèi)核程序

物理內(nèi)存影射區(qū)

Vmalloc分配區(qū)

slab

get_free_page(s)

內(nèi)核頁表

get_free_page(s)

請頁異常

vmalloc分配的內(nèi)核虛擬內(nèi)存與kmalloc/get_free_page分配的內(nèi)核虛擬內(nèi)存位于不同的區(qū)間,不會重疊。因為內(nèi)核虛擬空間被分區(qū)管理,各司其職。進(jìn)程空間地址分布從0到G(其實是到PAGE_OFFSET,0x86中它等于0xC0000000),從3Gvmalloc_start這段地址是物理內(nèi)存映射區(qū)域(該區(qū)域中包含了內(nèi)核鏡像、物理頁框表mem_map等等)比如我使用的系統(tǒng)內(nèi)存是64M(可以用free看到),那么(3G——3G+64M)這片內(nèi)存就應(yīng)該映射物理內(nèi)存,而vmalloc_start位置應(yīng)在3G+64M附近(說附近因為是在物理內(nèi)存映射區(qū)與vmalloc_start期間還回存在一個8M大小的gap來防止躍界),vmalloc_end的位置接近4G(說接近是因為最后位置系統(tǒng)會保留一片128k大小的區(qū)域用于專用頁面映射,還由可能會由高端內(nèi)存映射區(qū),這些都是細(xì)節(jié),這里我們不做糾纏)。

 

 

 

進(jìn)程地址空間

物理內(nèi)存映射區(qū)

 

3G

內(nèi)核虛擬空間

Vmalloc_start

Vmalloc_end

上圖是內(nèi)存分布的模糊輪廓

 

   get_free_pageKmalloc函數(shù)所分配的連續(xù)內(nèi)存都陷于物理映射區(qū)域,所以它們返回的內(nèi)核虛擬地址和實際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),你可以很方便的將其轉(zhuǎn)化為物理內(nèi)存地址,同時內(nèi)核也提供了virt_to_phys()函數(shù)將內(nèi)核虛擬空間中的物理影射區(qū)地址轉(zhuǎn)化為物理地址。要知道,物理內(nèi)存映射區(qū)中的地址與內(nèi)核頁表是有序?qū)?yīng),系統(tǒng)中的每個物理頁框都可以找到它對應(yīng)的內(nèi)核虛擬地址(在物理內(nèi)存映射區(qū)中的)。

vmalloc分配的地址則限于vmalloc_startvmalloc_end之間。每一塊vmalloc分配的內(nèi)核虛擬內(nèi)存都對應(yīng)一個vm_struct結(jié)構(gòu)體(可別和vm_area_struct搞混,那可是進(jìn)程虛擬內(nèi)存區(qū)域的結(jié)構(gòu)),不同的內(nèi)核虛擬地址被4k打大小空閑區(qū)的間隔,以防止越界——見下圖)。與進(jìn)程虛擬地址的特性一樣,這些虛擬地址可與物理內(nèi)存沒有簡單的位移關(guān)系,必須通過內(nèi)核頁表才可轉(zhuǎn)換為物理地址或物理頁。它們有可能尚未被映射,在發(fā)生缺頁時才真正分配物理頁框。

 

這里給出一個小程序幫助大家認(rèn)請上面幾種分配函數(shù)所對應(yīng)的區(qū)域。

#include<linux/module.h>

#include<linux/slab.h>

#include<linux/vmalloc.h>

unsigned char *pagemem;

unsigned char *kmallocmem;

unsigned char *vmallocmem;

int init_module(void)

{

 pagemem = get_free_page(0);

 printk("<1>pagemem=%s",pagemem);

 kmallocmem = kmalloc(100,0);

 printk("<1>kmallocmem=%s",kmallocmem);

 vmallocmem = vmalloc(1000000);

 printk("<1>vmallocmem=%s",vmallocmem);

}

void cleanup_module(void)

{

 free_page(pagemem);

 kfree(kmallocmem);

 vfree(vmallocmem);

}

 

內(nèi)存管理實例

代碼功能介紹

我們希望能通過訪問用戶空間的內(nèi)存達(dá)到讀取內(nèi)核數(shù)據(jù)的目的,這樣便可進(jìn)行內(nèi)核空間到用戶空間的大規(guī)模信息傳輸。

具體的講,我們要利用內(nèi)存映射功能,將系統(tǒng)內(nèi)核中的一部分虛擬內(nèi)存映射到用戶空間,從而使得用戶空間地址等同與被映射的內(nèi)核內(nèi)存地址。

代碼結(jié)構(gòu)體系介紹

內(nèi)核空間內(nèi)存分配介紹

因此我們將試圖寫一個虛擬字符設(shè)備驅(qū)動程序,通過它將系統(tǒng)內(nèi)核空間映射到用戶空間——將內(nèi)核虛擬內(nèi)存映射到用戶虛擬地址。當(dāng)然映射地址時少不了定位內(nèi)核空間對應(yīng)的物理地址,并且還要建立新的用戶頁表項,以便用戶進(jìn)程尋址時能找到對應(yīng)的物理內(nèi)存。

從中應(yīng)該看出,需要我完成既定目標(biāo),我們需要獲得:被映射內(nèi)核空間物理地址 建立對應(yīng)的用戶進(jìn)程頁表。

在內(nèi)核空間中主要存在kmalloc分配的物理連續(xù)空間和vmalloc分配的非物理連續(xù)空間。kmalloc分配的空間往往被稱為內(nèi)核邏輯地址,由于它是連續(xù)分配(直接處理物理頁框),而且分配首地址一定,所以其分配的內(nèi)核虛擬地址對應(yīng)的實際物理地址很容易獲得:內(nèi)核虛擬地址—PAGE_OFFSET0xC0000000)(內(nèi)核有對應(yīng)例程virt_to_phys)即等于物理地址,而且其對應(yīng)的頁表屬于內(nèi)核頁表(swapper_pg_dir——在系統(tǒng)初始化時就以建立,因此省去了建立頁表的工作。

vmalloc分配的空間被稱為內(nèi)核虛擬地址,它的問題相對要復(fù)雜些,這是因為其分配的內(nèi)核虛擬內(nèi)存空間并非直接操作頁框,而是分配的是vm_struct結(jié)構(gòu)。該結(jié)構(gòu)邏輯上連續(xù)但對應(yīng)的物理內(nèi)存并非連續(xù),也就是說它vamlloc分配的內(nèi)核空間地址所對應(yīng)的物理地址并非可通過簡單線性運算獲得。從這個意義上講,它的物理地址在分配前是不確定的,因此雖然vmalloc分配的空間與kmalloc一樣都是由內(nèi)核頁表來映射的,但vmalloc分配內(nèi)核虛擬地址時必須更新內(nèi)核頁表。

 

注釋:vmalloc分配的內(nèi)核虛擬內(nèi)存與kmalloc/get_free_page分配的內(nèi)核邏輯內(nèi)存位于不同的區(qū)間,不會重疊。因為內(nèi)核空間被分區(qū)管理,各司其職。進(jìn)程空間地址分布從0到G(其實是到PAGE_OFFSET,0x86中它等于0xC0000000),從3Gvmalloc_start這段地址是物理內(nèi)存映射區(qū)域(該區(qū)域中包含了內(nèi)核鏡像、物理頁框表mem_map等等)比如我使用的系統(tǒng)內(nèi)存是64M(可以用free看到),那么(3G——3G+64M)這片內(nèi)存就應(yīng)該映射物理內(nèi)存,而vmalloc_start位置應(yīng)在3G+64M附近(說附近因為是在物理內(nèi)存映射區(qū)與vmalloc_start期間還回存在一個8M大小的gap來防止躍界),vmalloc_end的位置接近4G(說接近是因為最后位置系統(tǒng)會保留一片128k大小的區(qū)域用于專用頁面映射,還由可能會由高端內(nèi)存映射區(qū),這些都是細(xì)節(jié),這里我們不做糾纏)。

       另一個需要澄清的是,vmalloc分配的內(nèi)核空間,其結(jié)構(gòu)是vm_area,可千萬別與用戶空間malloc分配的vm_area_struct結(jié)構(gòu)混淆。前者由內(nèi)核頁表映射,而后者則由用戶頁表映射。

 

 

進(jìn)程地址空間

 

物理內(nèi)存映射區(qū)kmalloc分配

 

 Vmalloc 分配區(qū)

 

 

3Gpage_offset

 

內(nèi)核虛擬空間

 

Vmalloc_start

 

Vmalloc_end

 

上圖是內(nèi)存分布的模糊輪廓

 

實例藍(lán)圖

為了近可能豐富我們的例子程序的場景,我們選擇映射vmalloc分配的內(nèi)核虛擬空間(下面我們簡稱為vk地址)到用戶空間。

要知道用戶進(jìn)程操作的是虛擬內(nèi)存區(qū)域vm_area_struct,我們此刻需要將用戶vma區(qū)間利用用戶頁表映射到vk對應(yīng)的物理內(nèi)存上去(如下圖所示)。這里主要工作便是建立用戶也表項完成映射工作,而這個工作完全落在了vma->nopage[3]操作上,該方法會幫助我們在發(fā)生“缺頁”時,動態(tài)構(gòu)造映射所需物理內(nèi)存的頁表項。

 

 

用戶虛擬空間Vm_area_struct

Vk空間vm_struct

物理內(nèi)存

Vma->nopage

 

 

 

 

我們需要實現(xiàn)nopage方法,動態(tài)建立對應(yīng)頁表,而在該方法中核心任務(wù)是尋找到vk地址對應(yīng)的內(nèi)核邏輯地址[4]。這必然需要我們做以下工作:

a)         找到vmalloc虛擬內(nèi)存對應(yīng)的內(nèi)核頁表,并尋找到對應(yīng)的內(nèi)核頁表項。

b)        獲取內(nèi)核頁表項對應(yīng)的物理頁框指針。

c)        通過頁框得到對應(yīng)的內(nèi)核邏輯地址。

基本函數(shù)

我們實例將利用一個虛擬字符驅(qū)動程序,驅(qū)動負(fù)責(zé)將一定長的內(nèi)核虛擬地址(vmalloc分配的)映射到設(shè)備文件上,以便可以通過訪問文件內(nèi)容來達(dá)到訪問內(nèi)存的目的。這樣做的最大好處是提高了內(nèi)存訪問速度,并且可以利用文件系統(tǒng)的接口編程(設(shè)備在Linux中作為特殊文件處理)訪問內(nèi)存,降低了開發(fā)難度。

 

 Map_driver.c就是我們的虛擬字符驅(qū)動程序,不用說它要實現(xiàn)文件操作表(file_operations——字符驅(qū)動程序主要做的工作便是實現(xiàn)該結(jié)構(gòu)中的,為了要完成內(nèi)存映射,除了常規(guī)的open/release操作外,必須自己實現(xiàn)mmap操作,該函數(shù)將給定的文件映射到指定的地址空間上,也就是說它將負(fù)責(zé)把vmalloc分配的內(nèi)核地址映射到我們的設(shè)備文件上。

我們下面就談?wù)?/span>mmap操作的實現(xiàn)細(xì)節(jié):

文件操作的mmap操作是在用戶進(jìn)行系統(tǒng)調(diào)用mmap[5]時被執(zhí)行的,而且在調(diào)用前內(nèi)核已經(jīng)給用戶找到并分配了合適的虛擬內(nèi)存區(qū)域vm_area_struct,這個區(qū)域?qū)⒋砦募?nèi)容,所以剩下要做的便是如何把虛擬區(qū)域和物理內(nèi)存掛接到一起了,即構(gòu)造頁表。由于我門前面所說的原因,我們系統(tǒng)中頁表需要動態(tài)分配,因此不可使用remap_page_range函數(shù)一次分配完成,而必須使用虛擬內(nèi)存區(qū)域自帶的nopage方法,在現(xiàn)場構(gòu)造頁表。這樣以來,文件操作的mmap的方法只要完成“為它得到的虛擬內(nèi)存區(qū)域綁定對應(yīng)的操作表vm_operations”即可。于是主要的構(gòu)造工作就落在了vm_operations中的nopage方法上了。

Nopage方法中核心內(nèi)容上面已經(jīng)提到了是“尋找到vk地址對應(yīng)的內(nèi)核邏輯地址”,這個解析內(nèi)核頁表的工作是需要自己編寫輔助函數(shù)vaddr_to_kaddr來完成的,它所作的工作概括來講就是上文提到的a\b\c三條。

有關(guān)整個任務(wù)執(zhí)行路徑請看下圖。

STEP BY STEP

編譯map_driver.cmap_driver.o模塊,具體參數(shù)見Makefile

加載模塊 insmod map_driver.o

生成對應(yīng)的設(shè)備文件

1 /proc/devices下找到map_driver對應(yīng)的設(shè)備命和設(shè)備號:grep mapdrv /proc/devices

2 建立設(shè)備文件mknod  mapfile c 254 0  (在我系統(tǒng)里設(shè)備號為254

    利用maptest讀取mapfile文件,將取自內(nèi)核的信息(”ok”——我們在內(nèi)核中在vmalloc分配的空間中填放的信息)打印到用戶屏幕。

 

全部程序下載 mmap.tar (感謝Martin Frey,該程序主體出自他的靈感)

 

 

 

 

 

 



[1] 還有些情況必須要求內(nèi)存連續(xù),比如DMA傳輸中使用的內(nèi)存,由于不涉及頁機(jī)制所以必須連續(xù)分配。

[2] 這種存儲池的思想在計算機(jī)科學(xué)里廣泛應(yīng)用,比如數(shù)據(jù)庫連接池、內(nèi)存訪問池等等。

[3] 構(gòu)建用戶也表項,除了使用nopage一次一頁的動態(tài)構(gòu)造,還又一種方法remap_page_range可以一次構(gòu)造一段內(nèi)存范圍的也表項,但顯然這個方法時針對物理內(nèi)存連續(xù)被分配時使用的,而我們vk對應(yīng)的物理內(nèi)存并非連續(xù),所以這里使用nopage。

[4] 很多人一定會問,為什么不直接找到物理地址那,而要找內(nèi)核邏輯地址呢? 沒錯,我們本意應(yīng)該是獲得物理地址,但是為了利用內(nèi)核提供的一些現(xiàn)成的例程,如virt_to_page等(它們都是針對內(nèi)核邏輯地址而言的),我們不妨轉(zhuǎn)化成內(nèi)核邏輯地址來做,別忘了內(nèi)核邏輯地址與理地址僅僅相差一個偏移量。

[5] 系統(tǒng)調(diào)用mmap原形是void *mmap2(void *start, size_t length, int prot, int flags, int fd, off_t pgoff)

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多