日期:2014-05-16  浏览次数:20702 次

《Linux内核设计与实现》ch14

《Linux内核设计与实现》ch14
2010年12月08日
  进程地址空间
  内核除了管理本身的内存外,还必须管理进程的地址空间。Linux采用虚拟内存技术,因此系统中所有进程之间以虚拟方式共享内存。
  每个进程都有一个32或64位的平坦(flat,指地址范围是一个独立的连续区间)地址空间,大小取决于体系结构。
  通常每个进程都有唯一的这种平坦地址空间,而且地址空间之间互不相干。但是进程之间也可以选择共享地址空间,我们称这样的进程为线程。
  在地址空间中,我们往往更关心的是进程有权访问的虚拟内存地址空间,这些可被访问的合法地址区间被称为内存区域(memory area)。通过内核,进程可以给自己的地址空间动态的添加或减少内存区域。
  进程只能范围有效范围内的内存地址,同时必须遵循特定的访问属性,如只读、只写。
  内存区域可以包含各种内存对象:
  .代码段,可执行文件代码的内存映射;
  .数据段,可执行文件的已初始化的全局变量的内存映射;
  .bss段,未初始化全局变量;
  .用于进程的用户空间栈(不要和进程内核栈混淆,进程的内核栈独立存在并由内核维护);
  .C库或动态链接程序等共享库的代码段、数据段、bss段;
  .任何内存映射文件;
  .任何共享内存段;
  .任何匿名内存映射,比如有malloc()分配的内存;
  14.1内存描述符
  内核使用内存描述符结构体表示进程的地址空间,该结构体包含了和进程地址空间有关的全部信息。内存描述符由mm_struct结构体表示,定义在中。
  mm_users域记录正在使用该地址的进程数目;
  mm_count域是mm_struct结构体的主引用计数。只要mm_users不为0,那么后者为1。当mm_count的值为0时,说明已经没有任何指向该mm_struct结构体的引用了,这时该结构体会被销毁;
  mmap和mm_rb这两个不同的数据结构描述的对象是相同的:该地址空间中的全部内存区域。前者是链表(适合简单、高效的遍历)形式,后者是红-黑树(适合搜索指定元素)的形式;
  所有的mm_struct结构体都通过自身的mmlist域连接在一个双向链表中,该链表的首元素是init_mm内存描述符,它代表了init进程的地址空间。操作该链表的时候需要使用mmlist_lock锁来防止并发访问。
  14.1.1分配内存描述符
  在进程的进程描述符中,mm域存放着该进程使用的内存描述符,所以current->mm便指向当前进程中的内存描述符。
  fork()函数利用copy_mm()函数复制父进程的内存描述符,也就是current->mm域给其子进程,而子进程中的mm_struct结构体实际上是通过文件kernel/fork.c中的allocate_mm()宏从mm_cachep slab缓存中分配得到的。
  通常每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间。
  如果父进程希望和其子进程共享地址空间,可以在调用clone()时,设置CLONE_VM标志。我们把这样的进程叫做线程。
  14.1.2销毁内存描述符
  进程退出时,内核会调用exit_mm()函数,执行一些常规的销毁工作,同时更新一些统计量。
  14.1.3内核线程
  内核线程没有进程地址空间,也就没有相关的内存描述符,所以内核线程对应的进程描述符中mm域为空。这也正是内核线程的真实含义--没有用户上下文。
  14.2内存区域
  内存区域由vm_area_struct结构体描述,定义在文件中,内存区域在内核中也经常被称作虚拟内存区域或VMA。
  vm_area_struct结构体描述了指定地址空间内连续区间上的一个独立内存范围。内核将每个内存区域作为一个单独的内存对象管理(注意,一个进程地址空间可以有多个内存区域),每个内存区域都拥有一致的属性,比如访问权限等,另外,相应的操作也都一致。
  采用面向对象方法使VMA结构体可以代表多种类型的内存区域--比如内存映射文件或进程的用户控件栈等。
  每个VMA描述符都对应于进程地址空间中的唯一区间。
  vm_start域指向区间的首地址,vm_end域指向区间的尾地址之后的第一个字节,因此,vm_end-vm_start的大小便是内存区域的长度。注意,在同一个地址空间中,不同的内存区域不能重叠。
  vm_mm域指向和VMA相关的mm_struct结构体,注意每个VMA对其相关的mm_struct结构体来说是唯一的。所以即使连个独立的进程将同一个文件映射到各自的地址空间,它们分别都有一个vm_area_struct结构体来标志自己的内存区域。
  14.2.1   VMA标志
  VMA标志是一种位标志,定义于。它包含在vm_flags域内,标志了内存区域所包含的页面的行为和信息。和物理页的访问权限不同,VMA标志反映了内核处理页面所需要遵循的行为准则,而不是硬件要求。
  我们来了解一下其中有趣和重要的标志,VM_READ、VM_WRITE和VM_EXEC标志了内存区域中页面的读、写和执行权限。VM_SHARE指明了内存区域包含的映射是否可以在多进程间共享,如果该标志被设置,则我们称其为共享映射;否则称之为私有映射。VM_IO标志内存区域中包含有对设备I/O空间的映射。该标志通常在设备驱动程序执行mmap()函数进行I/O空间映射时才被设置。
  14.2.2   VMA操作
  vm_area_struct结构体中的vm_ops域指向与指定内存区域相关的操作函数表,内核使用表中的方法操作VMA。
  vm_area_struct作为通用对象代表了任何类型的内存区域,而操作表描述了针对特定的对象实例的特定方法。
  操作函数表由vm_operations_struct结构体表示,定义在文件中:
  struct vm_operations_struct
  {
  void        (* open)(struct vm_area_struct *);
  void        (* close)(struct vm_area_struct *);
  struct      page   *   (*nopage)(struct vm_area_struct   *, unsigned long, int);
  int          ( *populate)(struct vm_area_struct   *, unsigned   long, unsigned long, pgprot_t,   unsigned   long,   int);
  };
  void   open(struct vm_area_struct   *area) 当指定的内存区域被加入到一个地址空间时,该函数被调用。
  void   close(struct vm_area_struct *area) 当指定的内存区域从地址空间被删除时,该函数被调用。
  14.2.3   实际使用中的内存区域
  可以使用/proc文件系统和pmap(1)工具查看给定进程的内存空间可其中所含的内存区域。
  下面列出了进程号为1764的进程的地址空间中的全部区域:
  $pmap   1764
  xinetd(1764)
  08048000 (132 KB)      r-xp (08:02 392673)     /usr/sbin/xinetd
  08069000 (4 KB)          rw-p (08:02 392673)    /usr/sbin/xi