1、甚么是mm_struct???
內存描寫符也用1個結構體表示,這個結構體的名字叫做mm_struct(內存描寫符),linux就是通過mm_struct這個結構體來實現內存管理。 1個進程的虛擬地址空間主要由兩個數據結構來描寫,1個是最高層次的mm_struct,1個是較高層次的vm_ares_struct。最高層次的mm_struct結構描寫了1個進程的全部虛擬地址空間。較高層次的結構vm_area_struct描寫了虛擬地址空間的1個區間(簡稱虛擬區)。每一個進程只有1個mm_struct結構,在每一個進程的task_struct結構體中,有1個指向該進程的結構。可以說,mm_struct結構是對全部用戶空間的描寫。
在進程的task_struct結構體中包括1個指向mm_struct結構的指針,mm_struct用來描寫1個進程的虛擬地址空間。進程的mm_struct則包括裝入的可履行映像信息和進程的頁目錄指針pgd。該結構還包括有指向vm_area_struct結構的幾個指針,每一個vm_area_struct代表進程的1個虛擬地址區間。vm_area_struct結構含有指向vm_operations_struct結構的1個指針,vm_operations_struct描寫了在這個區間的操作。vm_operations_struct結構中包括的是函數指針,其中open、close分別用于虛擬區間的打開、關閉,而nopage用于當虛擬頁面不再物理內存而引發的”缺頁異常”時所調用的函數,當linux處理這1缺頁異常時,就能夠為新的虛擬內存分配實際的物理內存。
下面的linux管理內存的大致結構:
2、mm_struct
每一個進程都只有1個內存描寫符mm_struct。在每一個進程的task_struct結構中,有1個指向mm_struct的變量,這個變量常常是mm。
mm_struct是對進程的地址空間(虛擬內存)的描寫。1個進程的虛擬空間中可能有多個虛擬區間,對這些虛擬空間的組織方式有兩種,當虛擬區較少時采取單鏈表,由mmap指針指向這個鏈表,當虛擬區間多時采取紅黑樹進行管理,由mm_rb指向這棵樹。由于程序中用到的地址常常具有局部性,因此,最近1次用到的虛擬區間極可能下1次還要用到,因此把最近用到的虛擬區間結構放到高速緩存,這個虛擬區間就由mmap_cache指向。
指針pgt指向該進程的頁目錄(每一個進程都有自己的頁目錄),當調度程序調度1個程序運行時,就將這個地址轉換成物理地址,并寫入控制寄存器。
由于進程的虛擬空間及下屬的虛擬區間有可能在不同的上下文中遭到訪問,而這些訪問又必須互斥,所以在該結構中設置了用于P,V操作的信號量mmap_sem。另外,page_table_lock也是為類似的目的而設置。
雖然每一個進程只有1個虛擬空間,但是這個虛擬空間可以被別的進程來同享。如:子進程同享父進程的地址空間,而mm_user和mm_count就對其計數。
另外,還描寫了代碼段、數據段、堆棧段、參數段及環境段的起始和結束地址。
struct mm_struct
{
struct vm_area_struct *mmap; //指向虛擬區間(VMA)鏈表
struct rb_root mm_rb; //指向red_black樹
struct vm_area_struct *mmap_cache; //找到最近的虛擬區間
unsigned long(*get_unmapped_area)(struct file *filp,unsigned long addr,unsigned long len,unsigned long pgoof,unsigned long flags);
void (*unmap_area)(struct mm_struct *mm,unsigned long addr);
unsigned long mmap_base;
unsigned long task_size; //具有該結構體的進程的虛擬地址空間的大小
unsigned long cached_hole_size;
unsigned long free_area_cache;
pgd_t *pgd; //指向頁全局目錄
atomic_t mm_users; //用戶空間中有多少用戶
atomic_t mm_count; //對"struct mm_struct"有多少援用
int map_count; //虛擬區間的個數
struct rw_semaphore mmap_sem;
spinlock_t page_table_lock; //保護任務頁表和mm->rss
struct list_head mmlist; //所有活動mm的鏈表
mm_counter_t _file_rss;
mm_counter_t _anon_rss;
unsigned long hiwter_rss;
unsigned long hiwater_vm;
unsigned long total_vm,locked_vm,shared_vm,exec_vm;
usingned long stack_vm,reserved_vm,def_flags,nr_ptes;
unsingned long start_code,end_code,start_data,end_data; //代碼段的開始start_code ,結束end_code,數據段的開始start_data,結束end_data
unsigned long start_brk,brk,start_stack; //start_brk和brk記錄有關堆的信息,start_brk是用戶虛擬地址空間初始化,brk是當前堆的結束地址,start_stack是棧的起始地址
unsigned long arg_start,arg_end,env_start,env_end; //參數段的開始arg_start,結束arg_end,環境段的開始env_start,結束env_end
unsigned long saved_auxv[AT_VECTOR_SIZE];
struct linux_binfmt *binfmt;
cpumask_t cpu_vm_mask;
mm_counter_t context;
unsigned int faultstamp;
unsigned int token_priority;
unsigned int last_interval;
unsigned long flags;
struct core_state *core_state;
}
3、vm_area_struct
虛擬地址區間vm_area_struct是虛擬內存的1部份,內存描寫符mm_struct指向全部虛擬空間,而vm_area_struct只是指向了虛擬空間的1段。較高層次的結構vm_area_struct是由雙向鏈表鏈接起來的,它們是依照虛擬地址降序排序的,每一個這樣的結構都對應描寫1個相鄰的地址空間范圍。之所以這樣分隔是由于每一個虛擬區間可能來源不同,有的可能來自可履行映像,有的可能來自同享庫,而有的多是動態內存分配的內存區,所以對每一個由vm_area_struct結構所描寫的區間的處理操作和它前后范圍的處理操作不同,因此linux把虛擬內存分割管理,并利用了虛擬內存處理例程vm_ops來抽象對不同來源虛擬內存的處理方法。不同的虛擬區間其處理操作可能不同,linux在這里利用了面向對象的思想,即把1個虛擬區間看成是1個對象,用vm_area_struct描寫這個對象的屬性,其中的vm_operation結構描寫了在這個對象上的操作。
struct vm_area_struct
{
struct mm_struct *vm_mm; //虛擬地址區間所在的地址空間
unsigned long vm_start; //在vm_mm中的起始地址
unsigned long vm_end; //在vm_mm中的結束地址
struct vm_area_struct *vm_next; //指向下1個虛擬區間
pgprot_t vm_page_prot; //虛擬區間的存取極限
unsigned long vm_flags; //描寫對虛擬區間進行操作的標志
struct rb_node vm_rb;
union
{
struct
{
struct list_head list;
void *parent;
struct vm_area_struct *head;
} vm_set;
struct raw_prio_tree_node prio_tree_node;
} shared;
struct list_head anon_vma_node;
struct anon_vma *anon_vma;
const struct vm_operations_struct *vm_ops; //對這個區間進行操作的函數
unsigned long vm_pgoff;
struct file* vm_file;
void *vm_private_data;
unsigned long vm_truncate_count;
}
4、vm_operations_struct
vm_operations_struct結構中包好的是函數指針,其中open、close分別用于虛擬內存的打開、關閉,而nopage用于當虛擬內存頁面沒有實際的物理內存映照而引發的”缺頁異常”時所調用的函數指針。
struct vm_operations_struct
{
void (*open)(struct vm_area_strucr *area);
void (*close)(struct vm_area_struct *area);
struct page *(nopage)(struct vm_area_struct *area,unsigned long address,int unused);
}
5、linux虛擬內存管理數據結構圖(本來想自己畫的,結果發現這個圖真的不錯,所以直接粘過來):