最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

一文幫你搞定Linux內(nèi)核分析-內(nèi)存管理詳解圖(秒懂)

2022-03-21 21:11 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

一、內(nèi)核空間

1、頁

  • 頁(page)是內(nèi)核的內(nèi)存管理基本單位。(linux/mm_types.h)

  1. flags:頁標(biāo)志包含是不是臟的,是否被鎖定等等,每一位單獨(dú)表示一種狀態(tài),可同時表示出32種不同狀態(tài),定義在<linux/page-flags.h>

  2. _count:計數(shù)值為-1表示未被使用。

  3. virtual:頁在虛擬內(nèi)存中的地址,對于不能永久映射到內(nèi)核空間的內(nèi)存(比如高端內(nèi)存),該值為NULL;需要事必須動態(tài)映射這些內(nèi)存。

  • 盡管處理器的最小可尋址單位通常為字或字節(jié),但內(nèi)存管理單元(MMU,把虛擬地址轉(zhuǎn)換為物理地址的硬件設(shè)備)通常以頁為單位處理。內(nèi)核用struct page結(jié)構(gòu)體表示每個物理頁,struct page結(jié)構(gòu)體占40個字節(jié),假定系統(tǒng)物理頁大小為4KB,對于4GB物理內(nèi)存,1M個頁面,故所有的頁面page結(jié)構(gòu)體共占有內(nèi)存大小為40MB,相對系統(tǒng)4G,這個代價并不高。

2、區(qū)(zone)

  • 內(nèi)核把頁劃分在不同的區(qū)(zone)

  • 總共3個區(qū),具體如下:



  1. 執(zhí)行DMA操作的內(nèi)存必須從ZONE_DMA區(qū)分配

  2. 一般內(nèi)存,既可從ZONE_DMA,也可從ZONE_NORMAL分配,但不能同時從兩個區(qū)分配;

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。?!前100名進(jìn)群領(lǐng)取,額外贈送一份價值699的內(nèi)核資料包(含視頻教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)?


?

3、頁分配與釋放

  • 下面列舉所有的頁為單位進(jìn)行連續(xù)物理內(nèi)存分配,也稱為低級頁分配器:


  1. get_zeroed_page:對于用戶空間,這個方法能保障系統(tǒng)敏感數(shù)據(jù)不會泄露

  2. page_address: 把給定的頁轉(zhuǎn)換成邏輯地址


4、字節(jié)分配與釋放

  • kmalloc,vmalloc分配都是以字節(jié)為單位

(1) kmalloc

  • 該函數(shù)返回的是一個指向內(nèi)存塊的指針,其內(nèi)存塊大小至少為size,所分配的內(nèi)存在物理內(nèi)存中連續(xù)且保持原有的數(shù)據(jù)(不清零)

  • 其中部分flags取值說明:

  1. GFP_USER: 用于用戶空間的分配內(nèi)存,可能休眠;

  2. GFP_KERNEL:用于內(nèi)核空間的內(nèi)存分配,可能休眠;

  3. GFP_ATOMIC:用于原子性的內(nèi)存分配,不會休眠;典型原子性場景有中斷處理程序,軟中斷,tasklet等

  • kmalloc內(nèi)存分配最終總是調(diào)用__get_free_pages 來進(jìn)行實(shí)際的分配,故前綴都是GFP_開頭。 kmalloc分最多只能分配32個page大小的內(nèi)存,每個page=4k,也就是128K大小,其中16個字節(jié)用來記錄頁描述結(jié)構(gòu)。

  • kmalloc分配的是常駐內(nèi)存,不會被交換到文件中。最小分配單位是32或64字節(jié)。

kzalloc

  • kzalloc()等價于先用 kmalloc() 申請空間, 再用memset()來初始化,所有申請的元素都被初始化為0。

vmalloc

  • 該函數(shù)返回的是一個指向內(nèi)存塊的指針,其內(nèi)存塊大小至少為size,所分配的內(nèi)存是邏輯上連續(xù)的。kmalloc不同,該函數(shù)乜有flags,默認(rèn)是可以休眠的。

小結(jié):


編輯切換為居中


5、slab層 slab分配器的作用:

  1. 對于頻繁地分配和釋放的數(shù)據(jù)結(jié)構(gòu),會緩存它;

  2. 頻繁分配和回收比如導(dǎo)致內(nèi)存碎片,為了避免,空閑鏈表的緩存會連續(xù)的存放,已釋放的數(shù)據(jù)結(jié)構(gòu)又會放回空閑鏈表,不會導(dǎo)致碎片;

  3. 讓部分緩存專屬單個處理器,分配和釋放操作可以不加SMP鎖;

  • slab層把不同的對象劃分為高速緩存組,每個高速緩存組都存放不同類型的對象,每個對象類型對應(yīng)一個高速緩存。kmalloc接口監(jiān)理在slab層只是,使用一組通用高速緩存。 每個高速緩存都是用kmem_cache結(jié)構(gòu)來表示

  1. kmem_cache_crreate:創(chuàng)建高速緩存

  2. kmem_cache_destroy: 撤銷高速緩存

  3. kmem_cache_alloc: 從高速緩存中返回一個指向?qū)ο蟮闹羔?/p>

  4. kmem_cache_free:釋放一個對象

  • 實(shí)例分析: 內(nèi)核初始化期間,/kernel/fork.c的fork_init()中會創(chuàng)建一個名叫task_struct的高速緩存; 每當(dāng)進(jìn)程調(diào)用fork()時,會通過dup_task_struct()創(chuàng)建一個新的進(jìn)程描述符,并調(diào)用do_fork(),完成從高速緩存中獲取對象。

6、棧的靜態(tài)分配

  • 當(dāng)設(shè)置單頁內(nèi)核棧,那么每個進(jìn)程的內(nèi)核棧只有一頁大小,這取決于編譯時配置選項(xiàng)。 好處:

  1. 可以減少每個進(jìn)程內(nèi)存的消耗;

  2. 隨著機(jī)器運(yùn)行時間的增加,尋找兩個未分配的、連續(xù)的頁越來越困難,物理內(nèi)存碎片化不斷加重,那么給每個新進(jìn)程分配虛擬內(nèi)存的壓力也增大;

  3. 每個進(jìn)程的調(diào)用鏈在自己的內(nèi)核棧中,當(dāng)單頁棧選項(xiàng)被激活時,中斷處理程序可獲得自己的棧;

  • 任意函數(shù)必須盡量節(jié)省棧資源, 方法就是所有函數(shù)讓局部變量所占空間之和不要超過幾百字節(jié)。

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

  • 高端內(nèi)存中的頁不能永久地映射到內(nèi)核地址空間。

1.kmap:把給定page結(jié)構(gòu)映射到內(nèi)核地址空間;

  • 當(dāng)page位于低端內(nèi)存,函數(shù)返回該頁的虛擬地址

  • 當(dāng)page位于高端內(nèi)存,建立一個永久映射,再返回地址

2.kunmap: 永久映射的數(shù)量有限,應(yīng)通過kunmap及時解除映射

3.kmap_atomic: 臨時映射

4.kunmap_atomic: 解除臨時映射


8、每個CPU數(shù)據(jù)

  1. alloc_percpu: 給系統(tǒng)的每個處理器分配一個指定類型對象的實(shí)例,以單字節(jié)對齊;

  2. free_percpu: 釋放每個處理器的對象實(shí)例;

  3. get_cpu_var: 返回一個執(zhí)行當(dāng)前處理器數(shù)據(jù)的特殊實(shí)例,同時會禁止內(nèi)核搶占

  4. put_cpu_var: 會重新激活內(nèi)核搶占

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

  1. 減少了數(shù)據(jù)鎖定,每個CPU訪問自己CPU數(shù)據(jù)

  2. 大大減少緩存失效,失效往往發(fā)生在一個處理器操作某個數(shù)據(jù),而其他處理器緩存了該數(shù)據(jù),那么必須清理或刷新緩存。持續(xù)不斷的緩存失效稱為緩存抖動。

9、小結(jié)

  • 分配函數(shù)選擇:

  1. 連續(xù)的物理頁,使用低級頁分配器 或kmalloc();

  2. 高端內(nèi)存分配,使用alloc_pages(),返回page結(jié)構(gòu)指針; 想獲取地址指針,應(yīng)使用kmap(),把高端內(nèi)存映射到內(nèi)核的邏輯地址空間;

  3. 僅僅需要虛擬地址連續(xù)頁,使用vmalloc(),性能有所損失;

  4. 頻繁創(chuàng)建和撤銷大量數(shù)據(jù)結(jié)構(gòu),考慮建立slab高速緩存。

二、用戶空間

  • 用戶空間中進(jìn)程的內(nèi)存,往往稱為進(jìn)程地址空間。Linux采用虛擬內(nèi)存技術(shù)。

1、地址空間

  • 每個進(jìn)程都有一個32位或64位的地址空間,取決于體系結(jié)構(gòu)。 一個進(jìn)程的地址空間與另一個進(jìn)程的地址空間即使有相同的內(nèi)存地址,也彼此互不相干,對于這種共享地址空間的進(jìn)程稱之為線程。一個進(jìn)程可尋址4GB的虛擬內(nèi)存(32位地址空間中),但不是所有虛擬地址都有權(quán)訪問。對于進(jìn)程可訪問的地址空間稱為內(nèi)存區(qū)域。每個內(nèi)存區(qū)域都具有對相關(guān)進(jìn)程的可讀、可寫、可執(zhí)行屬性等相關(guān)權(quán)限設(shè)置。

  • 內(nèi)存區(qū)域可包含的對象:

  1. 代碼段(text section): 可執(zhí)行文件代碼

  2. 數(shù)據(jù)段(data section): 可執(zhí)行文件的已初始化全局變量(靜態(tài)分配的變量和全局變量)。

  3. bss段:程序中未初始化的全局變量,零頁映射(頁面的信息全部為0值)。

  4. 進(jìn)程用戶空間棧的零頁映射(進(jìn)程的內(nèi)核棧獨(dú)立存在并由內(nèi)核維護(hù))

  5. 每一個諸如C庫或動態(tài)連接程序等共享庫的代碼段、數(shù)據(jù)段和bss也會被載入進(jìn)程的地址空間

  6. 任何內(nèi)存映射文件

  7. 任何共享內(nèi)存段

  8. 任何匿名的內(nèi)存映射(比如由malloc()分配的內(nèi)存)

  • 這些內(nèi)存區(qū)域不能相互覆蓋,每一個進(jìn)程都有不同的內(nèi)存片段。

2、內(nèi)存描述符

  • 內(nèi)存描述符由mm_struct結(jié)構(gòu)體表示,(linux/sched.h)


  1. mm_users:代表正在使用該地址的進(jìn)程數(shù)目,當(dāng)該值為0時mm_count也變?yōu)?;

  2. mm_count: 代表mm_struct的主引用計數(shù),當(dāng)該值為0說明沒有任何指向該mm_struct結(jié)構(gòu)體的引用,結(jié)構(gòu)體會被撤銷。

  3. mmap和mm_rb:描述的對象都是相同的

  • mmap以鏈表形式存放, 利于高效地遍歷所有元素

  • mm_rb以紅黑樹形式存放,適合搜索指定元素


  • mmlist:所有的mm_struct結(jié)構(gòu)體都通過mmlist連接在一個雙向鏈表中,該鏈表的首元素是init_mm內(nèi)存描述符,它代表init進(jìn)程的地址空間。

  • 在進(jìn)程的進(jìn)程描述符(<linux/sched.h>中定義的task_struct結(jié)構(gòu)體)中,mm域記錄該進(jìn)程使用的內(nèi)存描述符。故current->mm代表當(dāng)前進(jìn)程的內(nèi)存描述符。

  • fork()函數(shù) 利用copy_mm函數(shù)復(fù)制父進(jìn)程的內(nèi)存描述符,子進(jìn)程中的mm_struct結(jié)構(gòu)體通過allcote_mm()從高速緩存中分配得到。通常,每個進(jìn)程都有唯一的mm_struct結(jié)構(gòu)體,即唯一的進(jìn)程地址空間。

  • 當(dāng)子進(jìn)程與父進(jìn)程是共享地址空間,可調(diào)用clone(),那么不再調(diào)用allcote_mm(),而是僅僅是將mm域指向父進(jìn)程的mm,即 tsk->mm = current->mm。

  • 相反地,撤銷內(nèi)存是exit_mm()函數(shù),該函數(shù)會進(jìn)行常規(guī)的撤銷工作,更新一些統(tǒng)計量。

3、內(nèi)核線程

  1. 沒有進(jìn)程地址空間,即內(nèi)核線程對應(yīng)的進(jìn)程描述符中mm=NULL

  2. 內(nèi)核線程直接使用前一個進(jìn)程的內(nèi)存描述符,僅僅使用地址空間中和內(nèi)核內(nèi)存相關(guān)的信息

4、虛擬內(nèi)存區(qū)域(VMA)

  • 虛擬內(nèi)存區(qū)域由vm_area_struct結(jié)構(gòu)體描述, 指定地址空間內(nèi)連續(xù)區(qū)間的一個獨(dú)立內(nèi)存范圍。 每個VMA代表不同類型的內(nèi)存區(qū)域。linux/mm_types.h

  • 每個內(nèi)存描述符對應(yīng)于進(jìn)程地址空間的唯一區(qū)間,vm_end - vm_start便是內(nèi)存區(qū)間的長度。

5、VMA操作

6、查看進(jìn)程內(nèi)存空間

  • 每行數(shù)據(jù)格式: 開始-結(jié)束 訪問權(quán)限 偏移 主設(shè)備號:次設(shè)備號 i節(jié)點(diǎn) 文件

  1. 設(shè)備表示為00:00, 索引節(jié)點(diǎn)標(biāo)示頁為0,這個區(qū)域就是零頁(所有數(shù)據(jù)全為零)

  2. 數(shù)據(jù)段和bss具有可讀、可寫但不可執(zhí)行權(quán)限;而堆??勺x、可寫、甚至可執(zhí)行

  • 也可通過工具pmap

7、內(nèi)存區(qū)域操作

  • find_vma 查看mm_struct所屬于的VMA,搜索第一個vm_end大于addr的內(nèi)存區(qū)域


  1. 檢查mmap_cache,查看緩存VMA是否包含所需地址,如果沒有找到,進(jìn)入2

  2. 通過紅黑樹搜索;

  • find_vma_prev 查看mm_struct所屬于的VMA,搜索第一個vm_end小于addr的內(nèi)存區(qū)域

  • struct vm_area_struct * find_vma_prev(struct mm_struct *mm, unsigned long addr, struct vm_area_struct **pprev)mmap

  1. 內(nèi)核使用do_mmap()創(chuàng)建一個新的線性地址區(qū)間,如果創(chuàng)建的地址區(qū)間和一個已存在的相鄰地址區(qū)間有相同的訪問權(quán)限,則將兩個區(qū)間合并為一個。

  2. mmap()系統(tǒng)調(diào)用獲取內(nèi)核函數(shù)do_mmap()的功能。

  3. do_mummap()從特定的進(jìn)程地址空間中刪除指定地址區(qū)間

  4. mummap()與 mmap功能相反。

頁表

  • 應(yīng)用程序操作的對象時映射到物理內(nèi)存之上的虛擬內(nèi)存,而處理器直接操作的是物理內(nèi)存。故應(yīng)用程序訪問一個虛擬地址時,需要將虛擬地址轉(zhuǎn)換為物理地址,然后處理器才能解析地址訪問請求,這個轉(zhuǎn)換工作通過查詢頁表完成。

  • Linux使用三級頁表完成地址轉(zhuǎn)換。


  1. 頂級頁表:頁全局目錄(PGD),指向二級頁目錄;

  2. 二級頁表:中間頁目錄(PMD),指向PTE中的表項(xiàng);

  3. 最后一級:頁表(PTE),指向物理頁面。

  • 多數(shù)體系結(jié)構(gòu),搜索頁表工作由硬件完成。每個進(jìn)程都有自己的頁表(線程會共享頁表)。為了加快搜索,實(shí)現(xiàn)了翻譯后緩沖器(TLB),作為將虛擬地址映射到物理地址的硬件緩存。還有寫時拷貝方式共享頁表,當(dāng)fork()時,父子進(jìn)程共享頁表,只有當(dāng)子進(jìn)程或父進(jìn)程試圖修改特定頁表項(xiàng)時,內(nèi)核才創(chuàng)建該頁表項(xiàng)的新拷貝,之后父子進(jìn)程不再共享該頁表項(xiàng)??梢?,利用共享頁表可以消除fork()操作中頁表拷貝所帶來的消耗。

進(jìn)程與內(nèi)存

  • 所有進(jìn)程都必須占用一定數(shù)量的內(nèi)存,這些內(nèi)存用來存放從磁盤載入的程序代碼,或存放來自用戶輸入的數(shù)據(jù)等。內(nèi)存可以提前靜態(tài)分配和統(tǒng)一回收,也可以按需動態(tài)分配和回收。

  • 對于普通進(jìn)程對應(yīng)的內(nèi)存空間包含5種不同的數(shù)據(jù)區(qū):

  1. 代碼段

  2. 數(shù)據(jù)段

  3. BSS段

  • 堆:動態(tài)分配的內(nèi)存段,大小不固定,可動態(tài)擴(kuò)張(malloc等函數(shù)分配內(nèi)存),或動態(tài)縮減(free等函數(shù)釋放);

  • 棧:存放臨時創(chuàng)建的局部變量;


進(jìn)程內(nèi)存空間

  • Linux采用虛擬內(nèi)存管理技術(shù),每個進(jìn)程都有各自獨(dú)立的進(jìn)程地址空間(即4G的線性虛擬空間),無法直接訪問物理內(nèi)存。這樣起到保護(hù)操作系統(tǒng),并且讓用戶程序可使用比實(shí)際物理內(nèi)存更大的地址空間。

  1. 4G進(jìn)程地址空間被劃分兩部分,內(nèi)核空間和用戶空間。用戶空間從0到3G,內(nèi)核空間從3G到4G;

  2. 用戶進(jìn)程通常情況只能訪問用戶空間的虛擬地址,不能訪問內(nèi)核空間虛擬地址。只有用戶進(jìn)程進(jìn)行系統(tǒng)調(diào)用(代表用戶進(jìn)程在內(nèi)核態(tài)執(zhí)行)等情況可訪問到內(nèi)核空間;

  3. 用戶空間對應(yīng)進(jìn)程,所以當(dāng)進(jìn)程切換,用戶空間也會跟著變化;

  4. 內(nèi)核空間是由內(nèi)核負(fù)責(zé)映射,不會跟著進(jìn)程變化;內(nèi)核空間地址有自己對應(yīng)的頁表,用戶進(jìn)程各自有不同額頁表。


內(nèi)存分配

  • 進(jìn)程分配內(nèi)存,陷入內(nèi)核態(tài)分別由brk和mmap完成,但這兩種分配還沒有分配真正的物理內(nèi)存,真正分配在后面會講。

  • brk: 數(shù)據(jù)段的最高地址指針_edata往高地址推

  1. 當(dāng)malloc需要分配的內(nèi)存<M_MMAP_THRESHOLD(默認(rèn)128k)時,采用brk;

  2. brk分配的內(nèi)存需高地址內(nèi)存全部釋放之后才會釋放。(由于是通過推動指針方式)

  3. 當(dāng)最高地址空間的空閑內(nèi)存大于M_TRIM_THRESHOLD時(默認(rèn)128k),執(zhí)行內(nèi)存緊縮操作;

  • do_mmap:在堆棧中間的文件映射區(qū)域找空閑的虛擬內(nèi)存

  1. 當(dāng)malloc需要分配的內(nèi)存>M_MMAP_THRESHOLD(默認(rèn)128k)時,采用do_map();

  2. mmap分配的內(nèi)存可以單獨(dú)釋放

8、物理內(nèi)存

  1. 物理內(nèi)存只有進(jìn)程真正去訪問虛擬地址,發(fā)生缺頁中斷時,才分配實(shí)際的物理頁面,建立物理內(nèi)存和虛擬內(nèi)存的映射關(guān)系。

  2. 應(yīng)用程序操作的是虛擬內(nèi)存;而處理器直接操作的卻是物理內(nèi)存。當(dāng)應(yīng)用程序訪問虛擬地址,必須將虛擬地址轉(zhuǎn)化為物理地址,處理器才能解析地址訪問請求。

  3. 物理內(nèi)存是通過分頁機(jī)制實(shí)現(xiàn)的

  4. 物理頁在系統(tǒng)中由也結(jié)構(gòu)struct page描述,所有的page都存儲在數(shù)組mem_map[]中,可通過該數(shù)組找到系統(tǒng)中的每一頁。

9、虛擬內(nèi)存 轉(zhuǎn)化為 真實(shí)物理內(nèi)存:

  1. 虛擬進(jìn)程空間:通過查詢進(jìn)程頁表,獲取實(shí)際物理內(nèi)存地址;

  2. 虛擬內(nèi)核空間:通過查詢內(nèi)核頁表,獲取實(shí)際物理內(nèi)存地址;

  3. 物理內(nèi)存映射區(qū):物理內(nèi)存映射區(qū)與實(shí)際物理去偏移量僅PAGE_OFFSET,通過通過virt_to_phys()轉(zhuǎn)化;

虛擬內(nèi)存與真實(shí)物理內(nèi)存映射關(guān)系:


  • 其中物理地址空間中除了896M(ZONE_DMA + ZONE_NORMAL)的區(qū)域是絕對的物理連續(xù),其他內(nèi)存都不是物理內(nèi)存連續(xù)。在虛擬內(nèi)核地址空間中的安全保護(hù)區(qū)域的指針都是非法的,用于保證指針非法越界類的操作,vm_struct是連續(xù)的虛擬內(nèi)核空間,對應(yīng)的物理頁面可以不連續(xù),地址范圍(3G + 896M + 8M) ~ 4G;另外在虛擬用戶空間中 vm_area_struct同樣也是一塊連續(xù)的虛擬進(jìn)程空間,地址空間范圍0~3G。


  • 碎片問題

  • 外部碎片:未被分配的內(nèi)存,由于太多零碎的不連續(xù)小內(nèi)存,無法滿足當(dāng)前較大內(nèi)存的申請要求;

  1. 原因:頻繁的分配與回收物理頁導(dǎo)致大量的小塊內(nèi)存夾雜在已分配頁面中間;

  2. 解決方案:伙伴算法有所改善

  • 內(nèi)部碎片:已經(jīng)分配的內(nèi)存,卻不能被利用的內(nèi)存空間;

  1. 緣由:所有內(nèi)存分配必須起始可被4、8或16(體系結(jié)構(gòu)決定)整除的地址或者M(jìn)MU分頁機(jī)制限制;

  2. 解決方案:slab分配器有所改善

  3. 實(shí)例:請求一個11Byte的內(nèi)存塊,系統(tǒng)可能會分配12Byte、16Byte等稍大一些的字節(jié),這些多余空間就產(chǎn)生碎片

整體內(nèi)存圖總結(jié)如下:


一文幫你搞定Linux內(nèi)核分析-內(nèi)存管理詳解圖(秒懂)的評論 (共 條)

分享到微博請遵守國家法律
安宁市| 榆林市| 武义县| 堆龙德庆县| 舞阳县| 宜丰县| 芷江| 万盛区| 修武县| 绥中县| 云安县| 林西县| 岳阳市| 巴东县| 河曲县| 台州市| 崇文区| 丰都县| 玛曲县| 廉江市| 巴里| 阿荣旗| 平邑县| 富蕴县| 永泰县| 萨迦县| 宝山区| 康马县| 织金县| 海城市| 洞口县| 米林县| 朔州市| 修文县| 读书| 克什克腾旗| 弥勒县| 灵丘县| 绥化市| 泉州市| 新乡市|