核心把物理頁作為記憶體管理的基本單位;記憶體管理單元(mmu)把虛擬位址轉換為物理
位址,通常以頁為單位進行處理。mmu以頁大小為單位來管理系統中的也表。
32位系統:頁大小4kb
64位系統:頁大小8kb
核心用相應的資料結構表示系統中的每個物理頁:
struct page
核心通過這樣的資料結構管理系統中所有的頁,因此核心判斷乙個頁是否空閒,誰有擁有這個頁
,擁有者可能是:使用者空間程序、動態分配的核心資料、靜態核心**、頁快取記憶體……
系統中每乙個物理頁都要分配這樣乙個結構體,進行記憶體管理。
linux記憶體定址存在問題:
一些硬體只能用某些特定的記憶體來執行dma(直接記憶體訪問)
一些體系結構其記憶體的物理定址範圍必須你定址範圍大得多。這樣導致一些記憶體不能永久對映到核心空間上。
通常32位linux核心位址空間劃分0~3g為使用者空間,3~4g為核心空間。當核心模組**或執行緒訪問記憶體時,
**中的記憶體位址都為邏輯位址,而對應到真正的物理記憶體位址,需要位址一對一的對映。因此核心空間位址為3~4g,
最多只能對映到1g空間的記憶體,超出1g大小的記憶體將如何去問呢!
zone_dma、zone_normal和zone_highmem。
zone_highmem即為高階記憶體,這就是記憶體高階記憶體概念的由來。
在x86結構中,三種型別的區域如下:
zone_dma 記憶體開始的16mb
zone_normal 16mb~896mb
zone_highmem 896mb ~ 結束
同樣每個區包含眾多頁,形成不同記憶體池,按照用途進行記憶體分配。
用相應的資料結構來表示區:
struct zone
static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)
該函式分配2的order次方個連續的物理頁,返回指向第乙個頁的page結構體指標。
void *page_address(const struct page *page)
返回指向給定物理頁當前所在的邏輯位址
extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
extern unsigned long get_zeroed_page(gfp_t gfp_mask);
釋放:extern void __free_pages(struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);
記憶體的分配可能失敗,記憶體的釋放要準確!
1 kmalloc
kmalloc()函式與使用者空間malloc一組函式類似,獲得以位元組為單位的一塊核心記憶體。
void *kmalloc(size_t size, gfp_t flags)
void kfree(const void *objp)
分配記憶體物理上連續。
gfp_t標誌:表明分配記憶體的方式。如:
gfp_atomic:分配記憶體優先順序高,不會睡眠
gfp_kernel:常用的方式,可能會阻塞。
2 vmalloc
void *vmalloc(unsigned long size)
void vfree(const void *addr)
vmalloc()與kmalloc方式類似,vmalloc分配的記憶體虛擬位址是連續的,而實體地址則無需連續,與使用者空間分配函式一致。
vmalloc通過分配非連續的物理記憶體塊,在修正頁表,把記憶體對映到邏輯位址空間的連續區域中,虛擬位址是連續的。
是否必須要連續的實體地址和具體使用場景有關。在不理解虛擬位址的硬體裝置中,記憶體區都必須是連續的。
通過建立頁表轉換成虛擬位址空間上連續,肯定存在一些消耗,帶來效能上影響。
所以通常核心使用kmalloc來申請記憶體,在需要大塊記憶體時使用vmalloc來分配。
核心中經常進行記憶體的分配和釋放。為了便於資料的頻繁分配和**,通常建立乙個空
閒鍊錶——記憶體池。當不使用的已分配的記憶體時,將其放入記憶體池中,而不是直接釋放掉。
linux核心提供了slab層來管理記憶體的分配和釋放。
頻繁分配和**必然導致記憶體碎片,快取他們.
slab層得設計實現
slab層把不同的物件劃分為所謂的快取記憶體組。每個快取記憶體組存放不同型別的物件。快取記憶體劃分為slab,
slab由乙個或多個物理上連續的頁組成。每個slab處於三種狀態之一:滿,部分滿,空。
快取記憶體,slab,物件之間的關係:
與傳統的記憶體管理模式相比, slab 快取分配器提供了很多優點。首先,核心通常依賴於對小物件的分配,
它們會在系統生命週期內進行無數次分配。slab 快取分配器通過對類似大小的物件進行快取而提供這種功能,
從而避免了常見的碎片問題。slab 分配器還支援通用物件的初始化,從而避免了為同一目而對乙個物件重複
進行初始化。最後,slab 分配器還可以支援硬體快取對齊和著色,這允許不同快取中的物件占用相同的快取行,
從而提高快取的利用率並獲得更好的效能。
slab資料結構和介面:
每個快取記憶體用kmem_cache結構來表示:
struct kmem_cache
快取區包含三種slab:滿,未滿,空閒
struct kmem_list3 ;
每乙個slab包含多個物件:
struct slab ;
核心函式 kmem_cache_create 用來建立乙個新快取。這通常是在核心初始化時執行的,或者在首次載入核心模組時執行。
struct kmem_cache *kmem_cache_create (
const char *name,
size_t size,
size_t align,
unsigned long flags,
void (*ctor)(void *))
name 引數定義了快取名稱,proc 檔案系統(在 /proc/slabinfo 中)使用它標識這個快取。
size 引數指定了為這個快取建立的物件的大小,
align 引數定義了每個物件必需的對齊。
flags 引數指定了為快取啟用的選項:
kmem_cache_create 的部分選項(在 flags 引數中指定)
slab_red_zone 在物件頭、尾插入標誌,用來支援對緩衝區溢位的檢查。
slab_poison 使用一種己知模式填充 slab,允許對快取中的物件進行監視(物件屬物件所有,不過可以在外部進行修改)。
slab_hwcache_align 指定快取物件必須與硬體快取行對齊。
ctor 和 dtor 引數定義了乙個可選的物件構造器和析構器。構造器和析構器是使用者提供的**函式。當從快取中分配新物件時,可以通過構造器進行初始化。
要從乙個命名的快取中分配乙個物件,可以使用 kmem_cache_alloc 函式。
void kmem_cache_alloc( struct kmem_cache *cachep, gfp_t flags );
這個函式從快取中返回乙個物件。注意如果快取目前為空,那麼這個函式就會呼叫 cache_alloc_refill 向快取中增加記憶體。
kmem_cache_alloc 的 flags 選項與 kmalloc 的
cachep:所建立的快取區
flags引數:
gfp_user 為使用者分配記憶體(這個呼叫可能會睡眠)。
gfp_kernel 從核心 ram 中分配記憶體(這個呼叫可能會睡眠)。
gfp_atomic 使該呼叫強制處於非睡眠狀態(對中斷處理程式非常有用)。
gfp_highuser 從高階記憶體中分配記憶體。
永久對映:可能會阻塞
void *kmap(struct page *page)
解除對映:
void kunmap(struct page *page)
臨時對映:不會阻塞
void *kmap_atomic(struct page *page)
l 連續的物理頁:kmalloc或者低階頁分配器
l 高階記憶體分配:alloc_pages 指向page結構指標,不是邏輯位址指標。再通過kmap()把高階位址記憶體對映到核心的邏輯位址空間。
l 頻繁建立和銷毀很多較大資料結構:建立slab快取區,提高物件分配和**效能。
linux高階記憶體:
linux slab 分配器剖析:
Linux核心學習筆記 核心同步
linux核心中執行的程式,時刻都要防止併發引起的競態。這將會導致資料結構被破壞,嚴重的時候會引起核心崩潰。所以核心同步技術對核心開發的驅動程式來說非常重要。不懂核心同步技術的人,是寫不出安全健壯的核心驅動程式來的。在學習核心同步技術之前需要掌握一下幾個概念。1 並行,併發與競態 在smp執行的li...
linux核心學習筆記
核心的配置 a.make s3c2410 deconfig b.make menuconfig 圖形化配置 c.使用廠家給出的配置 生成.config 編譯生成核心,使用如下命令 make vmlinux make uimage 帶頭部 真正核心 1 config 建立生成autoconf.h 供源...
Linux核心學習筆記
2.2 核心原始碼樹 arch 特定體系結構的原始碼 block crypto api crypto 核心原始碼文件 drivers 裝置驅動程式 firmware fs vfs和各種檔案系統 include 核心標頭檔案 init 核心引導和初始化 ipc 程序間通訊 kernel 像排程程式這樣...