昨天介紹完了damand page與 copy on write這兩個在現今系統常常能夠看見的機制之後,要更深入的走入 Linux中,看看理論中用到的虛擬記憶體在實際的程式碼中會以怎麼樣的形式出現,本章主要簡單的介紹VMA的結構與主要成員。
由 user process 方向說明,VMA 是 user process 裡一段 virtual address space ;virtual address space 是屬於某個行程的一份連續的記憶體空間,當然 VMA 也會是連續的空間。VMA 對 Linux 的主要好處是,可以記憶體的使用更有效率,並且更容易管理 user process address space。
從另一個觀念來看,VMA 可以讓 Linux kernel 以 process 的角度來管理 virtual address space。
底下先來看看跟VMA相關的結構。
VMA 的實作主要是為了能更有效率地管理記憶體,並且是基於 paging 系統之上所發展出的;VMA 是比原始 paging 理論更高階的記憶體管理方法。
在Linux中需要管理每個行程的所有記憶體區域以及他們對應的分頁映射,所以必須抽象出一個資料結構負責完成這方面的事情,這就是 mm_struct
,在行程控制快(PCB)的資料結構 task_sturct
中有個指針 mm 就是指向 mm_struct
的。
以下列出幾個 task_struct
內常見的類別
<include/linux/sched.h>
task_struct{
struct thread_info thread_info; // 基本資訊
struct mm_struct mm; // 虛擬記憶體描述符
struct fs_struct *fs; // 指向檔案系統資訊
struct files_struct *files; // 行程打開的檔案
struct signal_struct *signal; // 所接受的信號
....
}
mm_struct
也包含在其中,以下列出 mm_struct
比較會用到的項目。
<include/linux/mm_types.h>
struct mm_struct {
struct vm_area_struct *mmap; /* list of VMAs */
struct rb_root mm_rb;
u64 vmacache_seqnum; /* per-thread vmacache */
unsigned long (*get_unmapped_area) (struct file *filp,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags);
unsigned long mmap_base; /* base of mmap area */
pgd_t * pgd;
/**
* @mm_users: The number of users including userspace.
*
* Use mmget()/mmget_not_zero()/mmput() to modify. When this
* drops to 0 (i.e. when the task exits and there are no other
* temporary reference holders), we also release a reference on
* @mm_count (which may then free the &struct mm_struct if
* @mm_count also drops to 0).
*/
atomic_t mm_users;
/**
* @mm_count: The number of references to &struct mm_struct
* (@mm_users count as 1).
*
* Use mmgrab()/mmdrop() to modify. When this drops to 0, the
* &struct mm_struct is freed.
*/
atomic_t mm_count;
spinlock_t page_table_lock;
/* Protects page tables and some counters */
struct rw_semaphore mmap_sem;
struct list_head mmlist;
/* List of maybe swapped mm's.These
* are globally strung together off
* init_mm.mmlist, and are protected
* by mmlist_lock
*/
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
....
};
mmap
: 行程裡面所有的VMA會形成一個單向的鏈表,mmap
是這個鏈表的 head。mm_rb
: VMA紅黑樹的根節點。get_unmapped_area
:判斷虛擬記憶體是否還有足夠的空間,這個函數會返回一段未使用過的空間的起始位址。mmap_base
: 指向mmap
區域的起始地址。pge
:指向行程的第一層頁表。mm_users
:紀錄正在使用該行程地址空間的行程數目若有兩個執行緒共享行程的地址空間,那麼 mm_users
便會是2。mm_count
: mm_struct
有多少個行程正在使用,像是 fork()
後,子行程會與父行程共用位址。mmap_sem
:用來保護行程地址空間VMA的一個鎖。mmlist
: 所有 mm_struct
會連結的一個雙向鏈表,該鏈表的頭是 init_mm
。start_code
、end_code
: 程式段的起始地址與結束地址。start_data``end_data
: 資料段的起始地址與結束地址。start_brk
:目前heap的起始位址。brk
:目前heap中 VMA的結束位址。total_vm
: 已經使用的行程空間的總和。
VMA 是虛擬記憶體的重要資料結構,他的主要成員如下,
struct vm_area_struct {
/* The first cache line has the info for VMA tree walking. */
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end address
within vm_mm. */
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next, *vm_prev;
struct rb_node vm_rb;
/*
* Largest free memory gap in bytes to the left of this VMA.
* Either between this VMA and vma->vm_prev, or between one of the
* VMAs below us in the VMA rbtree and its ->vm_prev. This helps
* get_unmapped_area find a free area of the right size.
*/
unsigned long rb_subtree_gap;
/* Second cache line starts here. */
struct mm_struct *vm_mm; /* The address space we belong to. */
pgprot_t vm_page_prot; /* Access permissions of this VMA. */
unsigned long vm_flags; /* Flags, see mm.h. */
/*
* For areas with an address space and backing store,
* linkage into the address_space->i_mmap interval tree.
*/
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
/*
* A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
* list, after a COW of one of the file pages. A MAP_SHARED vma
* can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack
* or brk vma (with NULL file) can only be in an anon_vma list.
*/
struct list_head anon_vma_chain; /* Serialized by mmap_sem &
* page_table_lock */
struct anon_vma *anon_vma; /* Serialized by page_table_lock */
/* Function pointers to deal with this struct. */
const struct vm_operations_struct *vm_ops;
/* Information about our backing store: */
unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE
units */
struct file * vm_file; /* File we map to (can be NULL). */
void * vm_private_data; /* was vm_pte (shared mem) */
struct mempolicy *vm_policy; /* NUMA policy for the VMA */
...
} __randomize_layout;
vm_start
和 vm_end
:指定VMA在行程位址空間中的起始地址跟結束地址。vm_next
和 vm_prev
:行程的VMA被鏈接成一個鏈表。vm_rb
: VMA 作為一個節點加入紅黑數,每個行程的mm_struct資料結構中都有一顆紅黑數mm->mm_rb
。vm_mm
:指向VMA所屬進程的 mm_struct
資料結構。vm_page_prot
: VMA的存取權。vm_flags
:描述VMA的一組標誌。anon_vma_chain
和 anon_vma
: 用於管理RMAP, RMAP的部分後面會講到。vm_ops
: 指向VMA能做的行為,這些方法用在VMA執行各種操作。vm_pgoff
: 指定文件映射的偏移量,這個變量的單位不是字節,而是分頁的大小。vm_file
: 指向 file,描述一個被映射的文件。
這裡要特別注意一個特別的地方 __randomize_layout
,詳細解說來自 stackoverflow
As a side note, the Linux kernel implements a gcc plugin to introduce an attribute named randomize_layout. The goal is to use it in the definition of the structures to make the compiler randomize the order of the fields. Linux kernel uses it for the sake of security to counter attacks that need to know the layout of structures.
簡單來說,這個插件是為了把結構內的順序打散,藉以對抗外部的攻擊。
原始的定義出現在 include/linux/compiler-gcc.h
內,
#define __randomize_layout __attribute__((randomize_layout))
rb-tree -> 查找快速
randomized mem layout
ref: 奔跑吧!Linux內核