【Linux】【进程】进程管理task_struct,mm_struct,vm_area_struct
1task_struct
struct task_struct {// 进程idpid_t pid;// 用于标识线程所属的进程 pidpid_t tgid;// 内存描述符表示进程虚拟地址空间struct mm_struct *mm;// 进程打开的文件信息struct files_struct *files;.......... 省略 .......
2mm_struct
每个进程都有唯一的 mm_struct 结构体,也就是前边提到的每个进程的虚拟地址空间都是独立,互不干扰的。
struct mm_struct {struct vm_area_struct *mmap; //指向VMA对象的链表头struct rb_root mm_rb; //指向VMA对象的红黑树的根struct vm_area_struct *mmap_cache; // 指向最近找到的虚拟区间 //mmap_cache保存了上一次找到的vma,根据局部性原理,下一次要用到的vma正好是上次使用的vma的可能性是比较大的 pgd_t * pgd; //指向页全局目录atomic_t mm_users; // 正在使用该地址的进程数atomic_t mm_count; // 引用计数,为0时销毁atomic_long_t nr_ptes; /* PTE page table pages */ //进程页表数int map_count; /* number of VMAs */ //VMA的个数unsigned long hiwater_rss; /* High-watermark of RSS usage */unsigned long hiwater_vm; /* High-water virtual memory usage */unsigned long total_vm; /* Total pages mapped */ //进程地址空间的页数 //total_vm是进程地址空间的总大小(以page为单位)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 def_flags;unsigned long task_size;unsigned long start_code, end_code, start_data, end_data; //代码段,数据段等的地址unsigned long start_brk, brk, start_stack; //堆栈段的地址,start_stack表示用户态堆栈的起始地址,brk为堆的当前最后地址unsigned long mmap_base ;unsigned long arg_start, arg_end, env_start, env_end; //命令行参数的地址,环境变量的地址.....};
total_vm
total_vm 表示在进程虚拟内存空间中总共与物理内存映射的页的总数。(注意映射这个概念,它表示只是将虚拟内存与物理内存建立关联关系,并不代表真正的分配物理内存。)
locked_vm
被锁定不能换出的内存页总数
当内存吃紧的时候,有些页可以换出到硬盘上,而有些页因为比较重要,不能换出。
pinned_vm
pinned_vm 表示既不能换出,也不能移动的内存页总数。
data_vm
data_vm 表示数据段中映射的内存页数目
exec_vm
exec_vm 是代码段中存放可执行文件的内存页数目
stack_vm
stack_vm是栈中所映射的内存页数目,这些变量均是表示进程虚拟内存空间中的虚拟内存使用情况。
mmap_cache
mmap_cache保存了上一次找到的vma,根据局部性原理,下一次要用到的vma正好是上次使用的vma的可能性是比较大的,因此使用find_vma()函数查找vma时,会首先从mmap_cache中找,找到了就直接返回。
没找到再去mm_rb红黑树里面找:
对vma的操作除了查找,还有增加和删除。加载一个动态链接库或者通过mmap创建映射时,都需要在进程地址空间中增加一个vma。
task_size
进程的内存描述符 mm_struct 结构体中的 task_size 变量,task_size 定义了用户态和内核态地址空间分界线
start_code 和 end_code (代码段)
start_code 和 end_code 定义代码段的起始和结束位置,程序编译后的二进制文件中的机器码被加载进内存之后就存放在这里。
start_data 和 end_data (数据段)
start_data 和 end_data 定义数据段的起始和结束位置,二进制文件中存放的全局变量和静态变量被加载进内存中就存放在这里。
(BSS 段)
后面紧挨着的是 BSS 段,用于存放未被初始化的全局变量和静态变量,这些变量在加载进内存时会生成一段 0 填充的内存区域 (BSS 段), BSS 段的大小是固定的,
start_brk(堆)
下面就是 OS 堆了,在堆中内存地址的增长方向是由低地址向高地址增长, start_brk 定义堆的起始位置,brk 定义堆当前的结束位置。
我们使用 malloc 申请小块内存时(低于 128K),就是通过改变 brk 位置调整堆大小实现的。
mmap_base (内存映射区)
接下来就是内存映射区,在内存映射区内存地址的增长方向是由高地址向低地址增长,mmap_base 定义内存映射区的起始地址。进程运行时所依赖的动态链接库中的代码段,数据段,BSS 段以及我们调用 mmap 映射出来的一段虚拟内存空间就保存在这个区域。
start_stack(栈)
start_stack 是栈的起始位置在 RBP 寄存器中存储,栈的结束位置也就是栈顶指针 stack pointer 在 RSP 寄存器中存储。在栈中内存地址的增长方向也是由高地址向低地址增长。
3vm_area_struct
用于描述进程地址空间中的一段虚拟区域,每一个VMA都对应一个struct vm_area_struct。
每个 vm_area_struct 结构对应于虚拟内存空间中的唯一虚拟内存区域 VMA,vm_start 指向了这块虚拟内存区域的起始地址(最低地址),vm_start 本身包含在这块虚拟内存区域内。vm_end 指向了这块虚拟内存区域的结束地址(最高地址),而 vm_end 本身包含在这块虚拟内存区域之外,所以 vm_area_struct 结构描述的是 [vm_start,vm_end) 这样一段左闭右开的虚拟内存区域。
struct vm_area_struct {/* The first cache line has the info for VMA tree walking. */struct mm_struct *vm_mm; //指向进程的mm_structunsigned long vm_start; /* Our start address within vm_mm. */ //起始地址unsigned long vm_end; /* The first byte after our end addresswithin 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; //红黑树节点pgprot_t vm page_prot;unsigned long vm_flags,struct file * vm_file; /* File we map to (can be NULL). */ //指向文件的一个打开实例struct anon_vma *anon_vma; /* Serialized by page_table_lock */struct file * vm_file; /* File we map to (can be NULL). */unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZEunits */ void *vm_private data;/* was vm pte(shared mem)/* Function pointers to deal with this struct. */const struct vm operations struct *vm ops ,};
vm_page_prot 和 vm_flags
vm_page_prot 和 vm_flags 都是用来标记 vm_area_struct 结构表示的这块虚拟内存区域的访问权限和行为规范。
虚拟内存区域 VMA 由许多的虚拟页 (page) 组成,每个虚拟页需要经过页表的转换才能找到对应的物理页面。页表中关于内存页的访问权限就是由 vm_page_prot 决定的。
anon_vma,vm_file,vm_pgoff
anon_vma,vm_file,vm_pgoff 分别和虚拟内存映射相关,虚拟内存区域可以映射到物理内存上,也可以映射到文件中,映射到物理内存上我们称之为匿名映射,映射到文件中我们称之为文件映射。
当我们调用 malloc 申请内存时,如果申请的是小块内存(低于 128K)则会使用 do_brk() 系统调用通过调整堆中的 brk 指针大小来增加或者回收堆内存。
匿名映射
如果申请的是比较大块的内存(超过 128K)时,则会调用 mmap 在上图虚拟内存空间中的文件映射与匿名映射区创建出一块 VMA 内存区域(这里是匿名映射)。这块匿名映射区域就用 struct anon_vma 结构表示。
文件映射
当调用 mmap 进行文件映射时,vm_file 属性就用来关联被映射的文件。这样一来虚拟内存区域就与映射文件关联了起来。vm_pgoff 则表示映射进虚拟内存中的文件内容,在文件中的偏移。
进程地址空间中的各个VMA结构
部分转自:https://www.cnblogs.com/binlovetech/p/16824522.html
部分转自:https://www.cnblogs.com/LoyenWang/p/12037658.html