在上篇文章根據crash學習用戶空間程序內存佈局涉及到了VMA的相關操作,本節通過一個簡單的實例來深刻的學習下VMA是什麼,以及VMA是如何組織的。
先來看下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;
/* 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.
*
* For private anonymous mappings, a pointer to a null terminated string
* in the user process containing the name given to the vma, or NULL
* if unnamed.
*/
union {
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
const char __user *anon_name;
};
} ;
- vma_start代表的是此vma的開始地址
- vma_end代表的是此vma的結束地址
- 因爲vma會通過雙向鏈表鏈接在一起的,所以會存在vm_next和vm_prev指針
- 同時爲了查找方便,vma也通過紅黑樹組織在一起,vm_rb則是vma的紅黑樹節點
- vm_mm就是此vma所屬的mm_struct結構
- vm_page_prot意思是此vma所對應的權限,是否可讀可寫可執行等
我們還是借用這張圖,可以看到task_struct中的mm_struct中會存在mmap和mmap_rb成員。
struct mm_struct {
struct {
struct vm_area_struct *mmap; /* list of VMAs */
struct rb_root mm_rb;
u64 vmacache_seqnum; /* per-thread vmacache */
......
瞭解了VMA的組織數據後,我們還是通過昨天的例子來通過驅動模塊來獲取VMA各個段的信息
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/mm.h>
static int mpid=1;
static void print_vma(struct task_struct *task)
{
struct mm_struct *mm;
struct vm_area_struct *vma;
int count=0;
mm = task->mm;
printk("This mm_struct has %d vma\n", mm->map_count);
for(vma = mm->mmap; vma; vma=vma->vm_next){
printk("vma number %d: \n", ++count);
printk("Start address 0x%lx, End address 0x%lx\n", vma->vm_start, vma->vm_end);
}
printk("Code segment start=0x%lx, end=0x%lx\n"
"Data Segment start=0x%lx, end=0x%lx\n"
"Stack segment start=0x%lx\n",
mm->start_code, mm->end_code, mm->start_data, mm->end_data, mm->start_stack);
}
static int vma_start()
{
struct task_struct *task;
printk("Got the process id =%d\n", mpid);
for_each_process(task) {
if(task->pid == mpid){
printk("%s[%d]\n", task->comm, task->pid);
print_vma(task);
}
}
return 0;
}
static void vma_exit()
{
printk("print segment info module exit!\n");
}
module_init(vma_start);
module_exit(vma_exit);
module_param(mpid, int, 0);
我們通過獲取應用程序的pid,然後通過模塊參數傳遞到驅動模塊中,匹配到相同的pid,則將此進程的名字(comm字段),PID(pid)字段打印出來。同時獲取當前進程有多少個vma,打印各個vma的開始地址和結束地址。
通過maps命令獲取進程的各個vma信息
root:/data # cat /proc/4766/maps
00400000-0047c000 r-xp 00000000 103:23 6918 /data/vma
0048b000-0048e000 rw-p 0007b000 103:23 6918 /data/vma
0048e000-0048f000 rw-p 00000000 00:00 0
38382000-383a4000 rw-p 00000000 00:00 0 [heap]
78941af000-78941fb000 rw-p 00000000 00:00 0
78941fb000-78941fc000 r--p 00000000 00:00 0 [vvar]
78941fc000-78941fd000 r-xp 00000000 00:00 0 [vdso]
7fc0ed3000-7fc0f9d000 rw-p 00000000 00:00 0 [stack]
再看看我們的驅動程序的打印信息
[ 2432.979096] Got the process id =4766
[ 2432.979495] vma[4766]
[ 2432.979500] This mm_struct has 8 vma
[ 2432.979504] vma number 1:
[ 2432.979508] Start address 0x400000, End address 0x47c000
[ 2432.979511] vma number 2:
[ 2432.979515] Start address 0x48b000, End address 0x48e000
[ 2432.979518] vma number 3:
[ 2432.979522] Start address 0x48e000, End address 0x48f000
[ 2432.979525] vma number 4:
[ 2432.979529] Start address 0x38382000, End address 0x383a4000
[ 2432.979532] vma number 5:
[ 2432.979536] Start address 0x78941af000, End address 0x78941fb000
[ 2432.979539] vma number 6:
[ 2432.979543] Start address 0x78941fb000, End address 0x78941fc000
[ 2432.979547] vma number 7:
[ 2432.979551] Start address 0x78941fc000, End address 0x78941fd000
[ 2432.979554] vma number 8:
[ 2432.979558] Start address 0x7fc0ed3000, End address 0x7fc0f9d000
[ 2432.979564] Code segment start=0x400000, end=0x47b76f
Data Segment start=0x48b770, end=0x48d348
Stack segment start=0x7fc0f9ba00
通過這個例子我們就清晰的瞭解到各個vma是用來描述各個段的,各個段的信息通過vm_area_struct結構有詳細的描述。而且各個vma都是通過雙鏈錶鏈接在一起的。鏈表的主要作用是方便刪除增加;另外一種紅黑樹組織方式是爲了查找方便的。