用户态与内核态划分
用户态布局
unsigned long mmap_base; /* base of mmap area */
unsigned long total_vm; /* Total pages mapped */
unsigned long locked_vm; /* Pages that have PG_mlocked set */
unsigned long pinned_vm; /* Refcount permanently increased */
unsigned long data_vm; /* VM_WRITE & ~VM_SHARED & ~VM_STACK */
unsigned long exec_vm; /* VM_EXEC & ~VM_WRITE & ~VM_STACK */
unsigned long stack_vm; /* VM_STACK */
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
total_vm 数据页的数目
locked_vm 锁定不能换出
pinned_vm 不能换出也不能移动
data_vm 存放数据的页的数目
exec_vm 存放可执行文件的页的数目
stack_vm 栈所占的页的数目
stac_brk 堆的起始位置,brk 结束位置,malloc 通过改变 brk 申请内存
arg_start/end 参数列表的位置,env_start/end 环境变量位置,均在栈中最高地址地方
mmap_base 标识虚拟地址中用于内存映射的起始地址
空间从高地址像低地址增长
通过映射一块区域到物理内存,辅助实现 malloc 申请大内存块
映射一块区域到 so 文件,辅助加载动态链接库 so 文件
struct vm_area_struct *mmap; /* list of VMAs */
struct rb_root mm_rb;
单链表:串联区域,将所有内存映射空间联系在一起
红黑树:快速查找内存区域,辅助实现快速修改
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;
struct mm_struct *vm_mm; /* The address space we belong to. */
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;
struct file * vm_file; /* File we map to (can be NULL). */
void * vm_private_data; /* was vm_pte (shared mem) */
} __randomize_layout;
vm_rb 将此区域放到红黑树上
vm_ops 对内存区域可做的操作定义
anon_vma 映射到物理内存也叫匿名映射
vm_file 映射到文件时指定被映射的文件
通过 load_elf_binary 将 vm_area_struct 和内存区域关联起来。(起作用还有启动第一个进程时被 init,fork 完成之后调用 exec 运行二进制程序)
exec 运行二进制程序:解析 ELF 格式,建立内存映射
static int load_elf_binary(struct linux_binprm *bprm)
{
......
setup_new_exec(bprm);
......
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
executable_stack);
......
error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
elf_prot, elf_flags, total_size);
......
retval = set_brk(elf_bss, elf_brk, bss_prot);
......
elf_entry = load_elf_interp(&loc->interp_elf_ex,
interpreter,
&interp_map_addr,
load_bias, interp_elf_phdata);
......
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->start_data = start_data;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;
......
}
setup_new_exec 设置内存映射区 mmap_base
setup_arg_pages 设置栈的 vm_area_stuct
设置 mm->arg_start 执行栈底
设置 current->mm->start_stack
elf_map 将 ELF 文件中的代码部分映射到内存
set_brk 设置堆的 vm_area_struct
current->mm->start_brk = current->mm->brk,空堆
load_elf_interp 将依赖的 so 映射到内存中的映射区域
修改映射的情况
函数调用,改变栈顶指针
通过 malloc 申请堆内存空间
小内存分配 brk 改变堆结束位置
大内存分配 malloc 执行 mmap