天天看點

程序位址空間(Linux核心源碼分析)背景程序控制塊mm_struct核心線程與程序位址空間vm_area_structdo_mmap總結

背景

之前寫過關于記憶體管理源碼分析的部落格。大體介紹了什麼是頁、區、slab緩存,以及核心擷取、釋放頁的接口,配置設定、釋放slab緩存的接口。程序位址空間簡單的說就是使用者空間中程序的記憶體,我們叫這記憶體為程序位址空間。本篇部落格借助linux源碼大體分析程序位址空間的相關知識。

程序控制塊

既然我們要聊一聊程序位址空間,那麼不可避免的就要先聊一下程序控制塊,程序控制塊的概念想必大家不會陌生。一個程序是由一個程序控制塊來描述的。是以,可想而知,程序控制塊中一定包含有程序位址空間的描述結構體。這個描述程序位址空間的結構體就是mm_struct。先來看下task_struct結構體。(位于linux/sched.h中)

程式位址空間(Linux核心源碼分析)背景程式控制塊mm_struct核心線程與程式位址空間vm_area_structdo_mmap總結

這是截取了task_struct中的一部分代碼。

mm_struct

接下來我們就重點的看一下mm_struct結構體。此結構體是用于描述程序位址空間的,是以每個程序都有唯一的mm_struct結構體,即唯一的程序位址空間。我們來看一下此結構體的部分源碼(位于linux/mm_types.h中)。

程式位址空間(Linux核心源碼分析)背景程式控制塊mm_struct核心線程與程式位址空間vm_area_structdo_mmap總結

203行的mmap域與204行的mm_rb域描述的對象是相同的。為什麼要用不同的方式來組織相同的對象呢?原因在于,用mmap作為連結清單可以友善周遊所有元素;而mm_rb作為紅黑樹,适合于搜尋指定的元素。

215行的mm_users域與216行的mm_count域都是原子數,用于使用者引用此程序位址空間(也就是此結構體)的計數。差別在于,mm_users表示的是有多少程序在引用此程序位址空間,若其為3,則表示有3個線程共享此程序位址空間。mm_count表示的是如果有使用者使用此程序位址空間,那麼此域值為1,否則域為0。當mm_count為0時,此結構體就會被撤銷。

所有的mm_struct結構體都通過雙向連結清單mmlist域連接配接在一起。雙向連結清單的連結清單頭是init程序的程序位址空間。

mm_struct結構體的源碼就分析這麼多。

我們都知道一個程序有唯一的一個mm_struct結構體,即一個程序有唯一的一個程序位址空間。那麼,在建立一個新程序時,mm_struct是如何被建立的?

我之前有一篇部落格講過slab緩存的作用以及用法,提到過slab緩存的作用在于快速為常用的結構體配置設定空間。很顯然mm_struct是一個常用的結構體,新程序的mm_struct結構體是通過allocate_mm()宏(位于linux/fork.h中)從mm_cachep slab緩存中配置設定得來的。前面提到過,一個程序有唯一的程序位址空間,而線程沒有自己的程序位址空間,不同線程共享同一個程序位址空間。是否共享位址空間幾乎是程序和Linux中線程間本質上的唯一差別。那麼,核心線程與程序位址空間的關系是如何的呢?

核心線程與程序位址空間

核心線程沒有程序位址空間,當然也就沒有mm_struct結構體。是以核心線程對應的程序描述符中的mm域為NULL。請大家回想一下在一個程序被排程時程序位址空間是如何進行轉換的?

實際上是該程序的mm域指向的程序位址空間被裝載到記憶體中,程序描述符中的active_mm域被更新指向新的程序位址空間。

而核心線程是沒有程序位址空間的,是以其mm域為NULL,當核心線程被排程時,核心發現它的mm域為NULL,就會保留前一個程序的程序位址空間,更新核心線程程序描述符中的active_mm域指向前一個程序的程序位址空間。因為核心線程不通路使用者空間的記憶體,是以它們僅僅使用位址空間中和核心記憶體相關的資訊,這些資訊的含義和普通程序完全相同。

上面講過的mm_struct結構體是描述程序位址空間的,而通過分析源碼我們發現,mm_struct結構體并沒有涉及到具體的虛拟記憶體區域,如記憶體區間何起何止。但在mm_struct結構體的第一個域mmap指向的vm_area_struct結構體中描述了虛拟記憶體區域。我們來看看這個結構體。

vm_area_struct

程式位址空間(Linux核心源碼分析)背景程式控制塊mm_struct核心線程與程式位址空間vm_area_structdo_mmap總結

這裡截取了部分vm_area_struct的源碼。(位于linux/mm_types.h中)

其中136行的vm_start域是程序位址空間的起始位址,137行的vm_end域是結束位址。

135行的vm_mm域指向相關的mm_struct結構體。也就是此虛拟記憶體區域相關的程序位址空間。

聊完虛拟記憶體區域VMA,接下來就可以聊一下如何為一個程序位址空間建立一個VMA。

do_mmap

核心使用do_mmap函數建立一個新的線性位址空間,由于新建立的位址空間若是與原有的位址空間相鄰核心會自動進行合并,是以更嚴格的來說,do_mmap不一定總是建立新的VMA,而可能是擴充已有的VMA。來看看do_mmap的源碼(位于linux/mm.h中)

程式位址空間(Linux核心源碼分析)背景程式控制塊mm_struct核心線程與程式位址空間vm_area_structdo_mmap總結

函數參數中的file、offset、len域分别指要映射的檔案、要映射的起始偏移位址、映射的長度。

其中file域可以為空,這時指此次映射與檔案無關,這叫做匿名映射,如果是與檔案有關的,叫做檔案映射。

addr是可選參數,它指定搜尋空閑區域的起始位置。prot參數指定記憶體區域中頁面的通路權限。flag參數指定了VMA标志。

有do_mmap來建立新的位址空間,當然就有do_munmap來删除位址空間。該函數定義在mm/mmap.c中。這裡就不贅述了。

總結

本篇部落格先介紹了什麼是程序位址空間,然後介紹了其描述符mm_struct,一個程序對應一個程序位址空間,也就是說task_struct對應一個mm_struct。記憶體描述符mm_struct介紹完後聊了一下其與核心線程的關系,因為我們知道,核心線程是沒有程序位址空間的。是以核心線程使用前一個程序的mm_struct。接下來介紹了一下vm_area_struct結構體,它描述了虛拟記憶體區域,也就是程序位址空間中的記憶體區域。最後簡要說了一下建立線性位址空間的函數do_mmap。

繼續閱讀