6.內(nèi)存管理

內(nèi)存管理

在內(nèi)核中分配內(nèi)存不像在其他地方分配內(nèi)存那么容易。造成這種局面的因素很多扶平,根本原因是內(nèi)核本身不能像用戶空間那樣奢侈地使用內(nèi)存帆离。

1.頁

內(nèi)核把物理頁作為內(nèi)存管理的基本單位。內(nèi)存管理單元(MMU,管理內(nèi)存并把虛擬地址轉(zhuǎn)換為物理地址的硬件)通常以頁為單位结澄。體系結(jié)構(gòu)不同哥谷,支持的頁大小也不同。內(nèi)核用struct page結(jié)構(gòu)表示每個物理頁:

struct page {
         unsigned long flags;                                                      
         atomic_t count;                
         unsigned int mapcount;          
         unsigned long private;          
         struct address_space *mapping;  
         pgoff_t index;                  
         struct list_head lru;  
         void *virtual;                  
};

對上面重要變量說明:

  • flag的每一位單獨表示一個狀態(tài)麻献,標志定義在<linux/page-flags.h>
  • count存放頁的引用次數(shù)们妥,為0則是空閑頁
  • virtual是頁的虛擬地址

2.區(qū)

由于硬件限制,內(nèi)核對頁不能一視同仁勉吻。有些頁位于內(nèi)存特定的物理地址上监婶,不能用于一些特定的任務,因此內(nèi)核把頁劃分為不同的區(qū)(zone)齿桃。Linux必須處理如下兩種由于硬件缺陷而引起的內(nèi)存尋址問題:

  • 一些硬件只能用某些特定內(nèi)存來執(zhí)行DMA(直接內(nèi)存訪問)
  • 一些體系結(jié)構(gòu)的內(nèi)存物理尋址范圍比虛擬尋址范圍大的多惑惶,因此部分內(nèi)存永遠無法映射到內(nèi)核空間

因此Linux主要存在四種區(qū):

  • ZONE_DMA,包含的頁可以執(zhí)行DMA
  • ZONE_DMA32短纵,和ZONE_DMA不同在于带污,這些頁面只能被32位設備訪問,某些體系下該區(qū)比ZONE_DMA更大
  • ZONE_NORMAL踩娘,能夠正常映射的頁
  • ZONE_HIGHMEM刮刑,不能永久被映射到內(nèi)核空間地址的區(qū)

每個區(qū)都用struct zone表示,定義在<linux/mmzone.h>

struct zone {
         spinlock_t              lock;
         unsigned long           free_pages;
         unsigned long           pages_min, pages_low, pages_high;
         unsigned long           protection[MAX_NR_ZONES];
         spinlock_t              lru_lock;       
         struct list_head        active_list;
         struct list_head        inactive_list;
         unsigned long           nr_scan_active;
         unsigned long           nr_scan_inactive;
         unsigned long           nr_active;
         unsigned long           nr_inactive;
         int                     all_unreclaimable; 
         unsigned long           pages_scanned;    
         struct free_area        free_area[MAX_ORDER];
         wait_queue_head_t       * wait_table;
         unsigned long           wait_table_size;
         unsigned long           wait_table_bits;
         struct per_cpu_pageset  pageset[NR_CPUS];
         struct pglist_data      *zone_pgdat;
         struct page             *zone_mem_map;
         unsigned long           zone_start_pfn;
 
         char                    *name;
         unsigned long           spanned_pages;  
         unsigned long           present_pages;  
};

其中养渴,lock是自旋鎖防止該結(jié)構(gòu)被并發(fā)訪問;watermark數(shù)組持有該區(qū)的最小值泛烙、最低和最高水位值理卑;name是以NULL結(jié)尾的區(qū)名字,三個區(qū)名字為DMA蔽氨,NormalHighMem藐唠。

3.獲得頁

前面了解了頁和區(qū)的概念,下面講述如何請求和釋放頁鹉究。

請求頁

標志 描述
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绍妨,返回指向邏輯地址的指針

釋放頁

釋放頁需要謹慎润脸,只能釋放屬于你的頁柬脸。傳遞了錯誤的struct page或地址,毙驯,用了錯誤的order值都可能導致系統(tǒng)崩潰倒堕。

例如釋放8個頁:

free_pages(page, 3)

可以看到釋放過程與C語言的釋放內(nèi)存很相似的。

4.kmalloc()

上述的方法是對以頁為單位的連續(xù)物理頁爆价,而以字節(jié)為單位的分配垦巴,內(nèi)核提供的函數(shù)是kmalloc()。使用方法和malloc()類似铭段,只是多了一個flags參數(shù)魂那,其在<linux/slab.h>中聲明:

void * kmalloc(size_t size, gfp_t flags)

kmalloc()對應的函數(shù)就是kfree()kfree()聲明于<linux/slab.h>中:

void kfree(const void *ptr)

5.vmalloc()

vmalloc()kmalloc()工作方式類似稠项,但是kmalloc()使用的連續(xù)的物理地址涯雅。vmalloc()使用非連續(xù)的物理地址,該函數(shù)為了把物理上不連續(xù)的頁轉(zhuǎn)換為虛擬地址空間上連續(xù)的頁展运,必須專門建立頁表項活逆。

大多數(shù)情況下,一般硬件設備需要使用連續(xù)的物理地址拗胜,而軟件可以使用非連續(xù)的物理地址蔗候,但是大多數(shù)情況,為了性能提升埂软,內(nèi)核往往用kmalloc()更多锈遥。

vmalloc()函數(shù)聲明在<linux/vmalloc.h>中,定義在<mm/vmalloc.c>中勘畔。用法和用戶空間的malloc()相同:

void * vmalloc(unsigned long size)

釋放通過vmalloc()所獲得的內(nèi)存所灸,使用下面函數(shù):

void vfree(const void *addr)

6.slab層

分配和釋放數(shù)據(jù)結(jié)構(gòu)是所有內(nèi)核中最常用操作之一。為了便于數(shù)據(jù)的頻繁分配和回收炫七,編程人員常常會用到空閑鏈表爬立。空閑鏈表包含可供使用的、已經(jīng)分配好的數(shù)據(jù)結(jié)構(gòu)塊万哪。當代名需要一個新的數(shù)據(jù)結(jié)構(gòu)實例時侠驯,就可以從空閑鏈表中抓取一個,而不需要分配內(nèi)存奕巍,再把數(shù)據(jù)存放進去吟策。不需要這個數(shù)據(jù)結(jié)構(gòu)的實例時,就放回空閑鏈表的止,而不是釋放它檩坚。空閑鏈表相對于對象的高速緩存——快速存儲頻繁使用的對象類型(這個策略簡直是awesome!)。

沒有免費的蛋糕效床,對于空閑鏈表存在的主要問題是無法全局控制睹酌。當內(nèi)存緊缺時,內(nèi)核無法通知每個空閑鏈表剩檀,讓其收縮緩存的大小憋沿,以便釋放部分內(nèi)存。實際上沪猴,內(nèi)核根本就不知道任何空閑鏈表辐啄。因此未來彌補這個缺陷,Linux內(nèi)核提供了slab層(也就是所謂的slab分配器)运嗜。slab分配器扮演了通用數(shù)據(jù)結(jié)構(gòu)緩存層的角色壶辜。對于slab分配器設計需要考慮一下幾個原則:

  • 頻繁使用的數(shù)據(jù)結(jié)構(gòu)也會頻繁分配和釋放,因此應當緩存它們担租。
  • 頻繁分配和回收必然會導致內(nèi)存碎片砸民。為了避免這種情況,空閑鏈表的緩存會連續(xù)地存放奋救。因為已釋放的數(shù)據(jù)結(jié)構(gòu)又會放回空閑鏈表岭参,不會導致碎片。
  • 回收的對象可以立即投入下一次分配尝艘,因此演侯,對于頻繁的分配和釋放,空閑鏈表能夠提高其性能背亥。
  • 如果讓部分緩存專屬于單個處理器秒际,那么,分配和釋放就可以在不加SMP鎖的情況下進行狡汉。
  • 對存放的對象進行著色娄徊,以防止多個對象映射到相同的高速緩存行。

slab層把不同的對象劃分為所謂的高速緩存組轴猎,其中每個高速緩存都存放不同類型的對象嵌莉,每種對象類型對應一個高速緩存,例如一個高速緩存用于task_struct捻脖,一個用于struct inode。kmalloc()接口建立在slab層上中鼠,使用了一組通用高速緩存可婶。這些緩存又被分為slabs,slab由一個或多個物理上連續(xù)的頁組成援雇,一般情況下矛渴,slab也就僅僅由一頁組成。每個高速緩存可以由多個slab組成。每個slab都包含一些對象成員具温,這里的對象指的是被緩存的數(shù)據(jù)結(jié)構(gòu)蚕涤,每個slab處于三種狀態(tài)之一:滿,部分滿铣猩,空揖铜。當內(nèi)核的某一部分需要一個新的對象時,先從部分滿的slab中進行分配达皿。如果沒有部分滿的slab天吓,就從空的slab中進行分配。如果沒有空的slab峦椰,就要創(chuàng)建一個slab了龄寞。下圖給出高速緩存,slab及對象之間的關系:

高速緩存汤功、slab和對象關系

每個緩存都使用kmem_catche結(jié)構(gòu)表示物邑,結(jié)構(gòu)中包含3個鏈表。這些鏈表包含高速緩存所有的slab滔金。slab描述符struct slab用來描述每個slab:

struct slab {
        struct list_head  list;       /*滿色解,部分滿或空鏈表*/
        unsigned long     colouroff;  /*slab著色的偏移量*/
        void              *s_mem;     /*在slab中的第一個對象*/
        unsigned int      inuse;      /*已分配的對象數(shù)*/
        kmem_bufctl_t     free;       /*第一個空閑對象*/
};

slab層負責內(nèi)存緊缺情況下所有底層的對齊、著色鹦蠕、分配冒签、釋放和回收等。

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

在前面討論的分配例子钟病,不少可以分配到棧上萧恕。用戶空間可以奢侈地負擔很大的棧,而且棾澹空間還可以動態(tài)增長票唆,相反內(nèi)核空間不能——棧小而固定。給每個進程分配一個固定小棧屹徘,可以減小內(nèi)存消耗和棧管理任務負擔走趋。

進程的內(nèi)核棧大小既依賴體系結(jié)構(gòu),也和編譯時的選項有關噪伊。在任何一個函數(shù)中簿煌,都必須盡量節(jié)省棧資源。讓函數(shù)所有局部變量之后不要超過幾百字節(jié)(棧上分配大量的靜態(tài)分配是不理智的)鉴吹,棧溢出就會覆蓋掉臨近堆棧末端的數(shù)據(jù)姨伟。首先就是前面講的thread_info

8.每個CPU使用數(shù)據(jù)

支持SMP的操作系統(tǒng)使用每個CPU上的數(shù)據(jù)豆励,對于給定的處理器其數(shù)據(jù)是唯一的夺荒。一般而言,每個CPU的數(shù)據(jù)存放在一個數(shù)組內(nèi),數(shù)組中的每一項對應著系統(tǒng)上一個存在的處理器技扼,安裝當前處理器號就能確定這個數(shù)組的當前元素伍玖。

在Linux中引入了新的操作接口稱為percpu,頭文件<linux/percpu.h>聲明了所有接口操作例程剿吻,可以在文件mm/slab.c<asm/percpu.h>找到定義窍箍。

使用每個CPU數(shù)據(jù)的好處是:

  • 減少了數(shù)據(jù)鎖定
  • 大大減少了緩存失效,一個CPU操作另一個CPU的數(shù)據(jù)時和橙,必須清理另一個CPU的緩存并刷新仔燕,存在不斷的緩存失效。持續(xù)不斷的緩存失效稱為緩存抖動魔招。

這種方式的唯一安全要求就是禁止內(nèi)核搶占晰搀,同時注意進程在訪問每個CPU數(shù)據(jù)過程中不能睡眠——否則,喚醒之后可能已經(jīng)到其他處理器上了办斑。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末外恕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乡翅,更是在濱河造成了極大的恐慌鳞疲,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蠕蚜,死亡現(xiàn)場離奇詭異尚洽,居然都是意外死亡,警方通過查閱死者的電腦和手機靶累,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門腺毫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挣柬,你說我怎么就攤上這事潮酒。” “怎么了邪蛔?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵急黎,是天一觀的道長。 經(jīng)常有香客問我侧到,道長勃教,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任匠抗,我火速辦了婚禮荣回,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘戈咳。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布著蛙。 她就那樣靜靜地躺著删铃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪踏堡。 梳的紋絲不亂的頭發(fā)上猎唁,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音顷蟆,去河邊找鬼诫隅。 笑死,一個胖子當著我的面吹牛帐偎,可吹牛的內(nèi)容都是我干的逐纬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼削樊,長吁一口氣:“原來是場噩夢啊……” “哼豁生!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起漫贞,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤甸箱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后迅脐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芍殖,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年谴蔑,在試婚紗的時候發(fā)現(xiàn)自己被綠了豌骏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡树碱,死狀恐怖肯适,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情成榜,我是刑警寧澤框舔,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站赎婚,受9級特大地震影響刘绣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挣输,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一纬凤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撩嚼,春花似錦停士、人聲如沸挖帘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拇舀。三九已至,卻和暖如春蜻底,著一層夾襖步出監(jiān)牢的瞬間骄崩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工薄辅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留要拂,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓站楚,卻偏偏與公主長得像脱惰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子源请,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

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