Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)——內(nèi)存管理

內(nèi)核的內(nèi)存使用不像用戶空間那樣隨意,內(nèi)核的內(nèi)存出現(xiàn)錯誤時(shí)也只有靠自己來解決(用戶空間的內(nèi)存錯誤可以拋給內(nèi)核來解決)过吻。
所有內(nèi)核的內(nèi)存管理必須要簡潔而且高效疑故。

主要內(nèi)容

  1. 內(nèi)存的管理單元
  2. 獲取內(nèi)存的方法
  3. 獲取高端內(nèi)存
  4. 內(nèi)核內(nèi)存的分配方式

1. 頁

內(nèi)核把物理頁作為內(nèi)存管理的基本單位,內(nèi)存管理單元MMU以頁為單位進(jìn)行處理,從虛擬內(nèi)存的角度砚嘴,頁就是最小單位。

struct page {
    unsigned long flags;    /* 存放頁的狀態(tài),臟頁户敬?被鎖定?等各種狀態(tài)參見<linux/page-flags.h> */
    atomic_t _count;        /* 頁的引用計(jì)數(shù) */
    atomic_t _mapcount;    /* 已經(jīng)映射到mms的pte的個數(shù) *
    unsigned long private;        /* 此page作為私有數(shù)據(jù)時(shí)睁本,指向私有數(shù)據(jù) */
    struct address_space *mapping;    /* 此page作為頁緩存時(shí)尿庐,指向關(guān)聯(lián)的address_space */
    pgoff_t index;        /* Our offset within mapping. */
    struct list_head lru;    /* 將頁關(guān)聯(lián)起來的鏈表項(xiàng) */
    void *virtual;            /* 頁的虛擬地址 */
};

如果頁的大小是 8KB 的話,消耗的管理page內(nèi)存只有 20MB 左右呢堰。相對于 4GB 來說并不算很多抄瑟。

2. 區(qū)

頁是內(nèi)存管理的最小單元,但是并不是所有的頁對于內(nèi)核都一樣枉疼。
內(nèi)核將內(nèi)存按地址的順序分成了不同的區(qū)皮假,有的硬件只能訪問有專門的區(qū)鞋拟。

在x86(32位系統(tǒng))上

區(qū) 描述 物理內(nèi)存
ZONE_DMA DMA使用的頁 <16MB
ZONE_NORMAL 正常可尋址的頁 16~896MB
ZONE_HIGHMEM 動態(tài)映射的頁 >896MB

某些硬件只能直接訪問內(nèi)存地址惹资,不支持內(nèi)存映射贺纲,對于這些硬件內(nèi)核會分配 ZONE_DMA 區(qū)的內(nèi)存。
某些硬件的內(nèi)存尋址范圍很廣褪测,比虛擬尋址范圍還要大的多猴誊,那么就會用到 ZONE_HIGHMEM 區(qū)的內(nèi)存。

3. 獲得頁

方法 描述
alloc_page(gfp_mask) 只分配一頁侮措,返回指向頁結(jié)構(gòu)的指針
alloc_pages(gfp_mask, order) 分配 2^order 個頁懈叹,返回指向第一頁頁結(jié)構(gòu)的指針
__get_free_page(gfp_mask) 只分配一頁,返回指向其邏輯地址的指針
__get_free_pages(gfp_mask, order) 分配 2^order 個頁分扎,返回指向第一頁邏輯地址的指針
get_zeroed_page(gfp_mask) 只分配一頁澄成,讓其內(nèi)容填充為0,返回指向其邏輯地址的指針

alloc** 方法和 get** 方法的區(qū)別在于笆包,一個返回的是內(nèi)存的物理地址环揽,一個返回內(nèi)存物理地址映射后的邏輯地址。
如果無須直接操作物理頁結(jié)構(gòu)體的話庵佣,一般使用 get** 方法歉胶。

對應(yīng)的釋放函數(shù):

extern void __free_pages(struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);
extern void free_hot_page(struct page *page);

4. kmalloc()

獲得以字節(jié)為單位的一塊內(nèi)核內(nèi)存。

/**
 * @size  - 申請分配的字節(jié)數(shù)
 * @flags - 上面討論的各種 gfp_mask
 */
static __always_inline void *kmalloc(size_t size, gfp_t flags)
#+end_src

vmalloc的定義在 mm/vmalloc.c 中
#+begin_src C
/**
 * @size - 申請分配的字節(jié)數(shù)
 */
void *vmalloc(unsigned long size)

kmalloc 和 vmalloc 區(qū)別在于:

  • kmalloc 分配的內(nèi)存物理地址是連續(xù)的巴粪,虛擬地址也是連續(xù)的
  • vmalloc 分配的內(nèi)存物理地址是不連續(xù)的通今,虛擬地址是連續(xù)的

對應(yīng)的釋放方法:kfree, vfree

gfp_mask標(biāo)志

請求內(nèi)存時(shí),參數(shù)中有個標(biāo)志位肛根,控制分配內(nèi)存時(shí)必須遵守的一些規(guī)則辫塌。

  • 行為標(biāo)志 :控制分配內(nèi)存時(shí),分配器的一些行為
  • 區(qū)標(biāo)志 :控制內(nèi)存分配在那個區(qū)(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM 之類)
  • 類型標(biāo)志 :由上面2種標(biāo)志組合而成的一些常用的場景

5. slab層實(shí)現(xiàn)原理

頻繁的分配/釋放內(nèi)存必然導(dǎo)致系統(tǒng)性能的下降派哲,所以有必要為頻繁分配/釋放的對象內(nèi)心建立緩存臼氨。

linux中的高速緩存是用所謂的slab層來實(shí)現(xiàn)的

  1. 可以在內(nèi)存中建立各種對象的高速緩存(比如進(jìn)程描述相關(guān)的結(jié)構(gòu) task_struct 的高速緩存)
  2. 除了針對特定對象的高速緩存以外,也有通用對象的高速緩存
  3. 每個高速緩存中包含多個 slab芭届,slab用于管理緩存的對象
  4. slab中包含多個緩存的對象储矩,物理上由一頁或多個連續(xù)的頁組成
高速緩存-slab-對象.png

高速緩存被劃分為slab,每個slab都包含一些對象成員(被緩存的數(shù)據(jù)結(jié)構(gòu))褂乍,slab處于三種狀態(tài)之一(滿持隧、部分滿、空)
每個高速緩存都使用kmem_cache結(jié)構(gòu)來表示逃片,包含三個鏈表:slabs_full屡拨、slabs_partial和slabs_empty
,這些鏈表包含高速緩存中的所有slab。

struct slab {
    struct list_head list;   /* 存放緩存對象呀狼,這個鏈表有 滿裂允,部分滿,空 3種狀態(tài)  */
    unsigned long colouroff; /* slab 著色的偏移量 */
    void *s_mem;             /* 在 slab 中的第一個對象 */
    unsigned int inuse;         /* slab 中已分配的對象數(shù) */
    kmem_bufctl_t free;      /* 第一個空閑對象(如果有的話) */
    unsigned short nodeid;   /* 應(yīng)該是在 NUMA 環(huán)境下使用 */
};

slab層的應(yīng)用主要有四個方法:

  1. 高速緩存的創(chuàng)建
  2. 從高速緩存中分配對象
  3. 向高速緩存釋放對象
  4. 高速緩存的銷毀
/**
 * 創(chuàng)建高速緩存
 * 參見文件: mm/slab.c
 * 這個函數(shù)的注釋很詳細(xì)赠潦,這里就不多說了叫胖。
 */
struct kmem_cache *
kmem_cache_create (const char *name, size_t size, size_t align,
    unsigned long flags, void (*ctor)(void *))

/**
 * 從高速緩存中分配對象也很簡單
 * 函數(shù)參見文件:mm/slab.c
 * @cachep - 指向高速緩存指針
 * @flags  - 之前討論的 gfp_mask 標(biāo)志,只有在高速緩存中所有slab都沒有空閑對象時(shí)她奥,
 *           需要申請新的空間時(shí)瓮增,這個標(biāo)志才會起作用。
 *
 * 分配成功時(shí)哩俭,返回指向?qū)ο蟮闹羔? */
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)

/**
 * 向高速緩存釋放對象
 * @cachep - 指向高速緩存指針
 * @objp   - 要釋放的對象的指針
 */
void kmem_cache_free(struct kmem_cache *cachep, void *objp)

/**
 * 銷毀高速緩存
 * @cachep - 指向高速緩存指針 
 */
void kmem_cache_destroy(struct kmem_cache *cachep)

6. 在棧上的靜態(tài)分配

內(nèi)核空間的棧小而且固定
在x86體系結(jié)構(gòu)中绷跑,內(nèi)核棧的大小一般就是1頁或2頁,即 4KB ~ 8KB

內(nèi)核椃沧剩可以在編譯內(nèi)核時(shí)通過配置選項(xiàng)將內(nèi)核棧配置為1頁砸捏,配置為1頁的好處是分配時(shí)比較簡單,只有一頁隙赁,不存在內(nèi)存碎片的情況垦藏,因?yàn)橐豁撌潜揪褪欠峙涞淖钚挝弧?/p>

當(dāng)有中斷發(fā)生時(shí),如果共享內(nèi)核棧伞访,中斷程序和被中斷程序共享一個內(nèi)核棧會可能導(dǎo)致空間不足掂骏,
于是,每個進(jìn)程除了有個內(nèi)核棧之外厚掷,還有一個中斷棧弟灼,中斷棧一般也就1頁大小。

7. 高端內(nèi)存的映射

x86體系中冒黑,高于896MB的所有物理內(nèi)存的范圍大都是高端呢村田绑,不會永久或自動地映射到內(nèi)核地址空間。

  • 永久映射:void kmap(struct page page)
  • 臨時(shí)映射:void *kmap_atomic(struct page *page, enum km_type type)

8. 按CPU分配

按CPU來分配數(shù)據(jù)主要有2個優(yōu)點(diǎn):

  1. 最直接的效果就是減少了對數(shù)據(jù)的鎖抡爹,提高了系統(tǒng)的性能
  2. 由于每個CPU有自己的數(shù)據(jù)掩驱,所以處理器切換時(shí)可以大大減少緩存失效的幾率

如果一個處理器操作某個數(shù)據(jù),而這個數(shù)據(jù)在另一個處理器的緩存中時(shí)冬竟,那么存放這個數(shù)據(jù)的那個處理器必須清理或刷新自己的緩存欧穴。持續(xù)的緩存失效稱為緩存抖動,對系統(tǒng)性能影響很大诱咏。

percpu接口:

  • 編譯時(shí)分配
  • 運(yùn)行時(shí)分配,通過指針
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缴挖,一起剝皮案震驚了整個濱河市袋狞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖苟鸯,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件同蜻,死亡現(xiàn)場離奇詭異,居然都是意外死亡早处,警方通過查閱死者的電腦和手機(jī)湾蔓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砌梆,“玉大人默责,你說我怎么就攤上這事∠贪” “怎么了桃序?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長烂瘫。 經(jīng)常有香客問我媒熊,道長,這世上最難降的妖魔是什么坟比? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任芦鳍,我火速辦了婚禮,結(jié)果婚禮上葛账,老公的妹妹穿的比我還像新娘柠衅。我一直安慰自己,他們只是感情好注竿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布茄茁。 她就那樣靜靜地躺著,像睡著了一般巩割。 火紅的嫁衣襯著肌膚如雪裙顽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天宣谈,我揣著相機(jī)與錄音愈犹,去河邊找鬼。 笑死闻丑,一個胖子當(dāng)著我的面吹牛漩怎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嗦嗡,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼勋锤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了侥祭?” 一聲冷哼從身側(cè)響起叁执,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤茄厘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谈宛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體次哈,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年吆录,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窑滞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡恢筝,死狀恐怖哀卫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滋恬,我是刑警寧澤聊训,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站恢氯,受9級特大地震影響带斑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勋拟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一勋磕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敢靡,春花似錦挂滓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纺念,卻和暖如春贝椿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陷谱。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工烙博, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人烟逊。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓渣窜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宪躯。 傳聞我的和親對象是個殘疾皇子乔宿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容