转载:https://blog.csdn.net/weixin_42462202/article/details/102157454
这是一篇转载贴,原作者是上面这位,下文中用到的图片均是取自原作,我做了裁剪和整理,如果想要更细致的了解,请移步原文观看。
Linux内存管理
linux内存管理主要涉及到这样几件事,一个是虚拟内存管理,一个是物理内存管理,还有就是虚拟内存与物理内存之间、虚拟内存与文件系统之间的映射。
Linux进程空间的管理
在进程描述符task_struct内有一个用于管理虚拟内存的结构体mm_struct.
struct mm_struct **mm;
该结构体描述的是整个进程的虚拟内存状态信息。比如:用户空间的大小
unsigned long task_size;
32位操作系统下,用户空间为3G,内核空间是1G。
相应的数据结构为
struct mm_struct{
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; //参数列表和环境变量的位置
}
从上面的数据结构中可以看出,没有相应的分区描述信息。那么如何描述不同的分区呢?
有另外一个数据结构vm_area_strcut,用于描述和管理不同的分区。
struct vm_area_struct **mmap; //不同分区的描述信息
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. vma起始地址*/
unsigned long vm_end; /* The first byte after our end address within vm_mm. vma结束地址 */
/* linked list of VM areas per task, sorted by address 通过地址串接该进程所有vma的链表*/
struct vm_area_struct *vm_next, *vm_prev;
struct rb_node vm_rb; // vm_rb是红黑树节点,用于描述该vma在红黑树中的信息
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. 针对该vma的相应操作*/
const struct vm_operations_struct *vm_ops;
struct file * vm_file; /* File we map to (can be NULL). 映射到的文件,可以为NULL*/
void * vm_private_data; /* was vm_pte (shared mem) 是否共享*/
} __randomize_layout;
那么这些信息是如何被初始化的呢?或者说如何初始化一个进程的进程空间?这涉及到一个函数load_elf_binary(加载内核是它,启动第一个用户态进程init是它,fork完调用exec执行一个程序也是它).它一般会进行以下的一些操作。
- 调用set_new_exec设置内存映射区mmap_base
- 调用set_arg_pages设置栈的vm_area_struct,这里设置mm->area_start指向栈底
- elf_map会将elf文件中代码段映射到内存中
- set_brk设置堆的vm_area_struct,这里设置mm->start_brk=mm->brk,即堆为空
- load_elf_interp将依赖的so文件映射到内存中来
通过以上的相关操作不仅初始化了mm_struct的信息也初始化了vm_area_struct的信息。
那么最终会形成下面的虚拟内存布局形式,注意此时还未与物理内存进行映射。