目录
地址空间保存了什么?
页表到底是怎么存储的
我们都知道,我们进程看到的空间其实是虚拟内存,真正的内存是需要页表的映射才能找到真正的物理内存,那么我我们有两个问题的引出那么进程地址空间是保存了什么呢?页表是将全部地址都映射了对应到页表项吗?
地址空间保存了什么?
其实地址空间的每个区保存了两个地址。我们拿堆区做例子
mm_struct对堆区其实也只是保存了这个堆区使用的开始与结束,再mm_struct中其实有一个指针,指针指向着一个链双向边表的起始位置
vm_area_struct这个是个结构体
<include/linux/mm_types.h>
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 addresswithin vm_mm. */
/*[vm_start, vm_end) 这么个情况,表示一块虚拟内存空间*//* linked list of VM areas per task, sorted by address */struct vm_area_struct *vm_next, *vm_prev;
/*在mm->mmap链表中前后节点*/struct rb_node vm_rb;
/*插入到mm->mm_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;
/*以当前vma为根,左子树中最大可用虚拟内存区域的大小*//* Second cache line starts here. */struct mm_struct *vm_mm; /* The address space we belong to. */
/*同一进程所有的vma指向的vm_mm是相同的*/pgprot_t vm_page_prot; /* Access permissions of this VMA. */
/*访问权限*/unsigned long vm_flags; /* Flags, see mm.h. */
/*属性,详看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;};/** 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;
/*该vma操作函数,包括打开、关闭、建立映射三个函数*//* Information about our backing store: */unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZEunits */struct file * vm_file; /* File we map to (can be NULL). */
/*文件映射的文件信息,匿名映射为NULL*/void * vm_private_data; /* was vm_pte (shared mem) */atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMUstruct vm_region *vm_region; /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMAstruct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endifstruct vm_userfaultfd_ctx vm_userfaultfd_ctx;
#ifdef CONFIG_SPECULATIVE_PAGE_FAULTseqcount_t vm_sequence; /* Speculative page fault field */atomic_t vm_ref_count; /* see vma_get(), vma_put() */
#endif
} __randomize_layout;
mm_struct中有个指针mmap指向由vm_area_struct构成的双向链表的头节点。
堆的数据范围其实就是vm_area_struct的多个结构体。
页表到底是怎么存储的
页帧与页框
页帧就是源文件编译完成后存储在磁盘的存储单位,也是以进程地址空间的方式进行编译的。
页框就是物理内存存储单位也是以4kb为单位的存储方式。
计算得知,我们的物理内存页框一共有:1024*1024*1024*4/1024=100w个页框,实际就是1024*1024。
进程创建/打开文件步骤:
- 页表映射,查看对应数据是否在磁盘上。
- 发现对应数据存放在磁盘上,然后将数据加载到物理内存中
- 然后我们的将页表项的物理映射改为刚刚加载的物理内存地址
这其实就是缺页中断
但是有个问题,32位机器下,虚拟内存有2^32个地址,难道页表需要开辟2^32项吗?
假设一页项有10字节
2^32*10=40G。
但是我们的物理内存就4G,我去哪里搞剩下空间???所以页表其实也是一种复杂的结构,是一种多级页表映射关系。
随便写个虚拟地址0B0000 0000 0100 0000 0030 0000 0000 0012
这就是实际的页表,这就是虚拟地址的查询逻辑。
假设,一二级页表每个页项20字节
20*1024*1024+1024*20=20MB。一个进程页表最多用这么多空间,相比之前40G好太多了。
一个知识点,二级页表并不是全部都有1024个,而是用按需申请使用。而一级页表是一定需要的。树根都没了,其他都是扯淡。
所以20MB只是最大,而不是一定是