mm_struct(内存描述符)

文章目录

  • 一、引出概念
  • 二、Linux下数据大致是这样管理
  • 三、mm_struct
    • 一、Linux下查看mm_struct
    • 二、mm_struct是什么?
    • 三、mm_struct
  • 四、vm_area_struct
  • 五、vm_operations_struct
  • 六、图解(在网上看到的,画的很具体就用上了)

一、引出概念

先看代码

   #include
   #include
   #include
   
   int g_val = 0;
                                                                                                                                    
   int main(){
     pid_t pid;
     pid = fork();
    if(pid < 0){
      perror("fork error\n");
    }
    else if(pid == 0){
      printf("this is child process...........\n");
      printf("g_val:%d g_val'adress: %p\n",g_val,&g_val);
    }
    else{
      printf("this is parent process..........\n");
      printf("g_val:%d g_val'adress: %p\n",g_val = 100,&g_val);
      sleep(5);
    }
    return 0;
}

看一下结果
mm_struct(内存描述符)_第1张图片
我们看到g_val的值在父进程和子进程分别打印出不同的值,但是地址是相同的,我们所认知的是同一块物理地址空间数据是一定相同的,但是,此时是不同的。说明这块空间不为物理地址。根据冯诺依曼体系结构可知,我们向磁盘写入数据有两种方式:一、直接写入磁盘;二、先写入缓冲区(内存),再让CPU去执行存储到磁盘。CPU是一个高效的处理器,磁盘是读写速率较慢的存储器,所以为了高速进行,采取第二种写入方式。内存的数据是虚拟,不具永久性,断电就会消失。那么,这里面数据不同,但是,地址相同。所以两个进程是在内存中。我们知道task_struct结构体里有一项成员叫做mm_struct——虚拟地址空间。

二、Linux下数据大致是这样管理

mm_struct(内存描述符)_第2张图片

三、mm_struct

一、Linux下查看mm_struct

vim /usr/src/kernels/3.10.0-514.26.2.el7.x86_64/include/linux/mm_types.h
注:kernels不同对应路径里的3.10.0-514.26.2.el7.×86_64不同。

二、mm_struct是什么?

操作系统运用了面向对象的思路对mm_struct进行封装,Linux就是通过mm_struct实现了内存管理。一个进程的虚拟地址空间主要由两个数据结构进行描述,一个是mm_struct,另一个是vm_area_struct。mm_struct描述的是虚拟地址的整体空间,vm_area_struct描述的是虚拟地址空间的一个区间(子集)。可以说,mm_struct结构是对整个用户空间的描述。

在进程的task_struct结构体中包含一个指向mm_struct结构的指针,mm_struct用来描述一个进程的虚拟地址空间。进程的mm_struct则包含装入的可执行映像信息以及进程的页表目录指针pgd。该结构还包含有指向vm_area_struct结构的几个指针,每个vm_area_struct代表进程的一个虚拟地址区间。vm_area_struct结构含有指向vm_operations_struct结构的一个指针,vm_operations_struct描述了在这个区间的操作。vm_operations_struct结构中包含的是函数指针,其中open、close分别用于虚拟区间的打开、关闭,而nopage用于当虚拟页面不再物理内存而引起的”缺页异常”时所调用的函数,当Linux处理这一缺页异常时,就可以为新的虚拟内存分配实际的物理内存。

三、mm_struct

每个进程都只有一个内存描述符mm_struct。在每个进程的task_struct结构中,有一个指向mm_struct的变量,这个变量常常是mm。
mm_struct是对进程的地址空间(虚拟内存)的描述。一个进程的虚拟空间中可能有多个虚拟区间,对这些虚拟空间的组织方式有两种,当虚拟区较少时采用单链表,由mmap指针指向这个链表,当虚拟区间多时采用红黑树进行管理,由mm_rb指向这棵树。因为程序中用到的地址常常具有局部性,因此,最近一次用到的虚拟区间很可能下一次还要用到,因此把最近用到的虚拟区间结构放到高速缓存,这个虚拟区间就由mmap_cache指向。
指针pgt指向该进程的页表目录(每个进程都有自己的页表目录),当调度程序调度一个程序运行时,就将这个地址转换成物理地址,并写入控制寄存器。
由于进程的虚拟空间及下属的虚拟区间有可能在不同的上下文中受到访问,而这些访问又必须互斥,所以在该结构中设置了用于P,V操作的信号量mmap_sem。此外,page_table_lock也是为类似的目的而设置。
虽然每个进程只有一个虚拟空间,但是这个虚拟空间可以被别的进程来共享。如:子进程共享父进程的地址空间,而mm_user和mm_count就对其计数。
另外,还描述了代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。

398 struct mm_struct {
  399   struct vm_area_struct * mmap;   /* list of VMAs */    //虚拟地址空间结构体,双向链表包含红黑树节点访问到不能访问的区域。
  400   struct rb_root mm_rb;                                 //红黑树的根节点
  401   struct vm_area_struct * mmap_cache; /* last find_vma result */  //mmap的高速缓冲器,指的是mmap最后指向的一个虚拟地址区间
  402 #ifdef CONFIG_MMU                                                                                                              
  403   unsigned long (*get_unmapped_area) (struct file *filp,
  404         unsigned long addr, unsigned long len,
  405         unsigned long pgoff, unsigned long flags);
  406   void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
  407 #endif
  408   unsigned long mmap_base;    /* base of mmap area */                                       //mmap区域的基地址
  409   unsigned long mmap_legacy_base;         /* base of mmap area in bottom-up allocations */  //自底向上的配置
  410   unsigned long task_size;    /* size of task vm space */                                   //进程的虚拟地址空间大小
  411   unsigned long cached_hole_size;   /* if non-zero, the largest hole below free_area_cache */ //缓冲器的最大的大小
  412   unsigned long free_area_cache;    /* first hole of size cached_hole_size or larger */       //不受约束的空间大小
  413   unsigned long highest_vm_end;   /* highest vma end address */                             //虚拟地址空间最大结尾地址
  414   pgd_t * pgd;                                                                              //页表的全局目录
  415   atomic_t mm_users;      /* How many users with user space? */                             //有多少用户
  416   atomic_t mm_count;      /* How many references to "struct mm_struct" (users count as 1) */ //有多少用户引用mm_struct
  417   atomic_long_t nr_ptes;  /* Page table pages */                                       //页表
  418   int map_count;        /* number of VMAs */                                               //虚拟地址空间的个数
  419 
  420   spinlock_t page_table_lock;   /* Protects page tables and some counters */               //保护页表和用户
  421   struct rw_semaphore mmap_sem;                                                            //读写信号
  422 
  423   struct list_head mmlist;    /* List of maybe swapped mm's.  These are globally strung
  424              * together off init_mm.mmlist, and are protected
  425              * by mmlist_lock
  426              */
  427 
  428 
  429   unsigned long hiwater_rss;  /* High-watermark of RSS usage */                     //标志
  430   unsigned long hiwater_vm; /* High-water virtual memory usage */
  431 
  432   unsigned long total_vm;   /* Total pages mapped */
  433   unsigned long locked_vm;  /* Pages that have PG_mlocked set */
  434   unsigned long pinned_vm;  /* Refcount permanently increased */
  435   unsigned long shared_vm;  /* Shared pages (files) */
  436   unsigned long exec_vm;    /* VM_EXEC & ~VM_WRITE */                                                                          
  437   unsigned long stack_vm;   /* VM_GROWSUP/DOWN */
  438   unsigned long def_flags;
  439   unsigned long start_code, end_code, start_data, end_data;               //开始代码段,结束代码。开始数据,结束数据
  440   unsigned long start_brk, brk, start_stack;                              //堆的开始和结束。
  441   unsigned long arg_start, arg_end, env_start, env_end;                   //参数的起始和结束,环境变量的起始和终点
  442 
  443   unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
        ...
};

四、vm_area_struct

内存描述符mm_struct指向整个地址空间,vm_area_struct只是指向了虚拟空间的一段。vm_area_struct是由双向链表链接起来的,它们是按照虚拟地址降序排序的,每个这样的结构都对应描述一个地址空间范围。之所以这样分隔是因为每个虚拟区间可能来源不同,有的可能来自可执行映像,有的可能来自共享库,而有的可能是动态内存分配的内存区,所以对于每个由vm_area_struct结构所描述的区间的处理操作和它前后范围的处理操作不同,因此linux把虚拟内存分割管理,并利用了虚拟内存处理例程vm_ops来抽象对不同来源虚拟内存的处理方法。不同的虚拟区间其处理操作可能不同,linux在这里利用了面向对象的思想,即把一个虚拟区间看成是一个对象,用vm_area_struct描述这个对象的属性,其中的vm_operation结构描述了在这个对象上的操作。

  281 struct vm_area_struct {
  282   /* The first cache line has the info for VMA tree walking. */    //第一个高速缓存行包含VMA树便利的信息
  283 
  284   unsigned long vm_start;   /* Our start address within vm_mm. */ //虚拟地址空间vm_mm起始地址
  285   unsigned long vm_end;   /* The first byte after our end address  
  286              within vm_mm. */                                     //在vm_mm中结束地址的第一个字节
  287 
  288   /* linked list of VM areas per task, sorted by address */       
  289   struct vm_area_struct *vm_next, *vm_prev;                       //双向链表连接以便于遍历
  290 
E>291   struct rb_node vm_rb;                                           //红黑树节点
  292 
  293   /*
  294    * Largest free memory gap in bytes to the left of this VMA.     //VMA左边最大的可用内存缺口
  295    * Either between this VMA and vma->vm_prev, or between one of the  
  296    * VMAs below us in the VMA rbtree and its ->vm_prev. This helps   //要么在当前的VMA和vma的前一个,要么是在VMA红黑树和它的前                                                                              一个中的VMAs之间
  297    * get_unmapped_area find a free area of the right size.        //这样有助于get_unmapped_area找到合适的空闲区域              
  298    */
  299   unsigned long rb_subtree_gap;                                  //红黑树子树的缺口
  300 
  301   /* Second cache line starts here. */
  302 
 302 
  303   struct mm_struct *vm_mm;  /* The address space we belong to. */   //进程拥有的地址空间
E>304   pgprot_t vm_page_prot;    /* Access permissions of this VMA. */   //此VMA访问权限
  305   unsigned long vm_flags;   /* Flags, see mm.h. */                  //标志
  306 
  307   /*
  308    * For areas with an address space and backing store,                                                                        
  309    * linkage into the address_space->i_mmap interval tree, or
  310    * linkage of vma in the address_space->i_mmap_nonlinear list.  //作为地址空间和后备存储区域,链接到间隔树或者链接到非线性链                                                                           表里的vma
  311    */
  312   union {
  313     struct {
  314       struct rb_node rb;
  315       unsigned long rb_subtree_last;
  316     } linear; //线性
  317     struct list_head nonlinear;  //链表非线性
  318   } shared;
  319 
  320   /*
  321    * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
  322    * list, after a COW of one of the file pages.  A MAP_SHARED vma
  323    * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
  324    * or brk vma (with NULL file) can only be in an anon_vma list. //MAP_SHARED只能                           在i_mmap树。匿名的对象,栈或者堆只能存在链表                                                                           里
  325    */
  326   struct list_head anon_vma_chain; /* Serialized by mmap_sem &
  327             * page_table_lock */      
  328   struct anon_vma *anon_vma;  /* Serialized by page_table_lock */
  329 
  330   /* Function pointers to deal with this struct. */
  331   const struct vm_operations_struct *vm_ops;               //虚拟内存
  332 
  333   /* Information about our backing store: */
  334   unsigned long vm_pgoff;   /* Offset (within vm_file) in PAGE_SIZE
  335              units, *not* PAGE_CACHE_SIZE */
  336   struct file * vm_file;    /* File we map to (can be NULL). */    //指向的文件
  337   void * vm_private_data;   /* was vm_pte (shared mem) */
  		...
  };                                                                                                                        

五、vm_operations_struct

vm_operations_struct结构中封装的是函数指针,其中open、close分别是虚拟操作的打开、关闭,而nopage相当于default用于当虚拟内存页面没有实际的物理内存映射而引起的”缺页异常”时所调用的函数指针。

struct vm_operations_struct
{
     void (*open)(struct vm_area_strucr *area);
     void (*close)(struct vm_area_struct *area);
     struct page *(nopage)(struct vm_area_struct *area,unsigned long address,int unused);
}

六、图解(在网上看到的,画的很具体就用上了)

mm_struct(内存描述符)_第3张图片

你可能感兴趣的:(理解)