linux核心學習筆記之記憶體管理

2021-08-18 11:25:48 字數 4668 閱讀 2923

linux核心把物理頁作為記憶體管理的基本單位。每一頁的大小根據系統架構不同有所區別,32位系統下為4kb,64位系統下為8kb。記憶體管理單元(mmu)以頁為單位來管理系統中的頁表,負責虛擬位址到實體地址的轉換,使用者所使用的記憶體位址一般都是虛擬位址。

核心中頁的結構體中比較重要的成員為:

struct page ;

記憶體中區的劃分:

名稱

物理記憶體

描述

zone_dma             

0~16mb

dma使用的記憶體

zone_normal

16~896mb

正常可定址的記憶體

zone_highem

896mb之後的記憶體

動態對映的記憶體

區的劃分實際是和體系結構相關,如x86體系結構下,isa裝置只能在物理記憶體的前16mb執行dma。所以在某些體系結構下,zone_dma和zone_normal可能合併為zone_normal。高階記憶體zone_highem也是類似。不同的區組成了不同的記憶體池,記憶體池的主要作用是減少記憶體碎片。

核心中區也有對應的結構體(省略部分):

struct zone lru[nr_lru_lists];

struct zone_reclaim_stat reclaim_stat;

unsigned long        pages_scanned;       /* since last reclaim */

unsigned long        flags;           /* zone flags, see below */

/* zone statistics */

atomic_long_t        vm_stat[nr_vm_zone_stat_items];

int prev_priority;

unsigned int inactive_ratio;

unsigned long        wait_table_hash_nr_entries;

unsigned long        wait_table_bits;

struct pglist_data    *zone_pgdat;

/* zone_start_pfn == zone_start_paddr >> page_shift */

unsigned long        zone_start_pfn;

unsigned long        spanned_pages;    /* total size, including holes */

unsigned long        present_pages;    /* amount of memory (excluding holes) */

const char        *name;

}核心中總共就三個區,所以也只有3個這樣的結構,name分別為「dma」、「normal」、「highmem」。

頁的操作介面:

static inline struct page *

alloc_pages(gfp_t gfp_mask, unsigned int order)            請求多個記憶體頁,返回指向第乙個頁的page結構體

void *page_address(struct page *page)         把給定的頁轉換成虛擬位址,返回虛擬位址

unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)      相當於上面兩個函式一起執行的結果,返回第乙個頁的邏輯位址

unsigned long get_zeroed_page(gfp_t gfp_mask)    請求填充為0的頁

void __free_pages(struct page *page, unsigned int order)          釋放頁,入參為page

void free_pages(unsigned long addr, unsigned int order)              釋放頁,入參為邏輯位址

我們常用的是以位元組為單位的分配,

位元組記憶體的操作介面:

void *kmalloc(size_t size, gfp_t flags) 用來分配實體地址連續的記憶體。

void kfree(const void *) 用來釋放kmalloc函式分配的記憶體。

void *vmalloc(unsigned long size)用來分配虛擬位址連續的記憶體,不要求實體地址連續。

void vfree(const void *addr)用於釋放vmalloc分配的記憶體

以上的記憶體分配介面都涉及到gfp_mask標誌,這裡不進行細說,例舉幾個常用的:

標誌

描述

gfp_kernel

常規分配方式,可能會阻塞。這個標誌在睡眠安全時用在程序上下文**中。一般為首選標誌。

gfp_atomic

用於中斷處理程式、下半部等不能睡眠的地方

gfp_dma

從zone_dma進行分配,通常與其他標誌組合使用

針對頻繁申請和釋放資料結構場景(如檔案描述符、程序描述符等)實現了slab分配器,slab分配器將不同的物件劃分快取記憶體組,每個快取記憶體組都存放不同的物件,每種物件型別對應乙個快取記憶體。kmalloc()介面建立在slab層之上,使用了一組通用快取記憶體。每個快取記憶體又可分為乙個或多個slab,通常乙個slab由乙個頁組成。

每個slab處於三種狀態之一:滿、部分滿、空。slab在核心中也對應著乙個結構體:

struct slab ;

slab描述有兩種存放的選擇:一種是存放在slab自身開始的地方,另一種是在slab之外另行分配。slab分配器建立新的slab時,可以kmem_get_pages()。使用kmem_freepages釋放記憶體。

建立快取記憶體介面如下:

struct kmem_cache *

kmem_cache_create (const char *name, size_t size, size_t align,

unsigned long flags, void (*ctor)(void *))

第乙個引數為快取記憶體名稱,第二個引數快取記憶體中每個元素的大小,第三個引數為slab內第乙個物件的偏移,通常為0。最後乙個是快取記憶體的建構函式,新頁追加到快取記憶體時才被呼叫。成功返回乙個指向快取記憶體的指標,失敗返回 null。

void kmem_cache_destroy(struct kmem_cache *);

撤銷快取記憶體

void *kmem_cache_alloc(struct kmem_cache *cachep,

gfp_t flags)  

從快取記憶體中分配物件,第二個引數通常是gfp_kernel或gfp_atomic

void kmem_cache_free(struct kmem_cache *cachep, void *objp)

釋放物件,放回slab中。第乙個為快取記憶體,第二個為需要釋放的物件位址

核心中也存在程序的概念,每個核心程序都有兩頁的核心棧。所以核心程序中盡量避免靜態分配,節省棧空間。

高階記憶體的頁不能永久對映到核心位址空間上,使用alloc_pages()+__gfp_highmem標誌來獲得的頁不具有虛擬位址,還需要使用kmap()將頁對映到核心位址空間,通常是3g~4g。該對映函式可以睡眠,只能用於程序上下文。使用kunmap()可以解除對映。在中斷上下文,可用kmap_atomic()來對映高階記憶體,kunmap_atomic()可以解除對映。

最後,核心實現了一種叫"per cpu"變數,我們稱為每cpu變數,實際就是定義了乙個陣列,長度為cpu數量,每乙個元素只能由對應的cpu處理。這樣可以避免併發訪問的問題,處理器可以不加鎖的情況下進行安全訪問。如:

long my_percpu[nr_cpus];

int cpu;

cpu=get_cpu();                //獲取當預處理器,並禁止核心搶占

my_percpu[cpu]++;

put_cpu()                     //啟用核心搶占

可以看到,在cpu訪問每cpu變數時,禁止了核心搶占,這是為了防止某些cpu可以接觸到其他cpu的資料。

define_per_cpu(type,name)        編譯時建立型別為type,名字為name的變數

declare_per_cpu(type,name)     宣告每cpu變數

get_cpu_var()                                返回當預處理器變數,同時禁止搶占,如:get_cpu_var()++;

put_cpu_var()                                啟用核心搶占

per_cpu(name,cpu)++                  增加指定cpu上name變數的值

void* alloc_percpu(type)                動態分配type型別的每cpu變數

void* __alloc_percpu(size_t size, size_t align)       增加了位元組對齊

void free_percpu(const void *);       釋放每cpu變數

核心學習之記憶體定址(一)

端選擇符和段暫存器 邏輯位址 48位 由段選擇符 16位 和偏移量 32位 組成 t1 0表示gdt,1表示ldt 段暫存器 16位 用來儲存段選擇符,由於段描述符為8位元組,因此,索引號只需要13位元組即可 cs 段暫存器,指向包含程式指令的段。它包含兩位的字段 cpl 0表示核心態,3表示使用者...

linux 核心學習之記憶體管理 未完待續

1 相關概念 mmu mmu是memory management unit的縮寫,中文名是 記憶體管理 單元,它是 處理器 cpu 中用來管理 虛擬儲存器 物理儲存器的控制線路,同時也負責 虛擬位址 對映為實體地址 以及提供硬體機制的記憶體訪問授權。頁 記憶體管理 物理頁 的基本單位 mmu通常以頁...

linux核心學習筆記(2) 記憶體定址

硬體中的分頁 頁框是乙個儲存區域,頁是乙個資料塊。兩者的長度一致 乙個 每個頁框包含乙個頁。4kb 1 directory 最高10位 2 table 中間10位 3 offset 最低12位 兩級頁表 兩級表的第一級表稱為頁目錄,儲存在乙個4k位元組的頁中,頁目錄表共有1k個表項,每個表項為4個位...