4.2 虚拟内存的管理
进程运行时能访问的存储空间只是它的虚拟内存空间。对当前进程而言只有属于它的虚拟内存是可见的。在进程的虚拟内存包含着进程本身的程序代码和数据。进程在运行中还必须得到操作系统的支持。进程的虚拟内存中还包含着操作系统内核。
Linux把进程的虚拟内存分成两部分,内核空间和用户空间。在Linux中,每个用户进程都可以访问4GB的线性虚拟内存空间。其中从0到3GB-1的虚存地址是用户空间,用户进程可以直接访问。从3GB到4GB-1的虚存地址为内核态空间,存放供操作系统内核访问的代码和数据,用户态进程不能访问。所有进程从3GB到4GB-1的虚拟空间都是一样的,Linux以此方式让内核态进程共享代码段和数据段。
操作系统内核的代码和数据等被映射到内核空间。进程的可执行映像(代码和数据)映射到虚拟内存的用户空间。进程虚拟内存的内核空间的访问权限设置为0级,用户空间为3级。内核访问虚存的权限为0级,而进程的访问权限为3级。
Linux运行在I386时,进程的虚拟内存为4GB。进程虚存空间的划分在系统初始化时由GDT确定,它定义在/arch/i386/kernel/head.S文件中。
Linux的存储管理主要是管理进程虚拟内存的用户区。进程虚拟内存的用户区分成代码段、数据段、堆栈以及进程运行的环境变量、参数传递区域等。
Linux用结构mm_struct描述了一个进程的整个虚拟地址空间。mm_struct结构包含了当前可执行文件信息和进程页目录指针pgd,以及指向vm_area_struc结构链表的指针。进程的task_struct内嵌了mm_struct的指针mm。
每一个进程,用一个mm_struct结构体来定义它的虚存用户区。mm_struct结构体首地址在任务结构体task_struct成员项mm中。mm_struct结构定义在/include/linux/schedul.h中(不同的版本结构略有不同)。
struct mm_struct
{
struct vm_area_struct * mmap; /*指向VMA链表表头的指针*/
rb_root_t mm_rb; /*指向进程红黑树的根*/
struct vm_area_struct * mmap_cache;
pgd_t * pgd; /*指向进程页目录表的指针*/
atomic_t mm_users; /*用户空间数*/
atomic_t mm_count; /* 访问mm_struct结构的计数*/
int map_count; /* VMA的数量 */
struct rw_semaphore mmap_sem;
spinlock_t page_table_lock; /*保护任务页表和mm-〉rss*/
struct list_head mmlist; /*所有活动mm的列表*/
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; /*分别为参数区、环境变量区的*首地址和终止地址*/
unsigned long rss, total_vm, locked_vm; /*驻留内存页框总数,VMA总数及被锁*VMA总数*/
unsigned long def_flags;
unsigned long cpu_vm_mask;
unsigned long swap_address;
unsigned dumpable:1;
mm_context_t context; /*和具体硬件结构有关的MM上下文*/
};
mm_struct结构描述了一个进程的页目录,有关进程的上下文信息,以及数据,代码,堆栈的起始、结束地址,还有虚拟存储器的数目,以及调度存储用的链表指针。
Linux在管理进程虚存空间时定义了虚存段(vma)。虚存段是进程一段连续的虚存空间,在这段虚存里,所有单元拥有相同特征。Linux用数据结构vm_area_struct描述了虚存段的属性,它主要包括:
(1)vma在虚存中的起始地址和终止地址。
(2)vma段内容来源,例如磁盘文件由其inode指示。
(3)一系列对vma操作例程。
(4)同一进程的vma段的vm_area_struct结构通过vm_next指针连接组成链表。系统以虚拟内存地址的降序排列vm_area_struct结构。这样建立了文件的逻辑地址到虚拟线性地址的映射。
vm_area-struct是描述进程的虚拟地址区域,一个虚存区域是虚存空间中一个连续的区域,在这个区域中的信息具有相同的操作和访问特性。它形成一个单向链表,这样当内核需要在一个给定进程页上执行给定操作时,可从双向列表中找到该项。每个虚拟区域用一个vm_area_struct结构体进行描述,它定义在/include/linux/mm.h中:
struct vm_area_struct {
struct mm_struct * vm_mm; /* vm_mm指针指向进程的mm_struct结构体*/
unsigned long vm_start; /*虚拟区域的开始地址*/
unsigned long vm_end; /*虚拟区域的终止地址*/
/*每个进程的虚存区链表,按地址排序*/
struct vm_area_struct vm_next; /*指向下一个vm_area_struct结构体,链表的首地址由*mm_struct中成员项mmap指出*/
pgprot_t vm_page_prot; /*该VMA的访问权限*/
unsigned short vm_flags; /*指出虚存区域的操作特性*/
struct rb_node vm_rb;
struct list_head shared;
struct vm_operations_struct * vm_ops; /*指向vm_operations_struct结构体的指针, *该结构体中包含着指向各种操作函数的指针*/
/* 后援存储器的信息*/
unsigned long vm_pgoff; /*PAGE_SIZE单元中的偏移量,不是PAGE_CACHE_SIZE*/
unsigned long vm_offset; /*该区域的内容相对于文件起始位置的偏移量,或相对于共*享内存首址的偏移量*/
struct file * vm_file;/* 若虚存区域映射的是磁盘文件或设备文件的内容,则vm_file *指向这个文件,否则为NULL*/
void * vm_private_data; /*共享内存页表vm_pte */
};
vm_flags指出了虚存区域的操作特性,参见表4-1。
表4-1 vm_flags的含义
所有vm_area_struct结构体链接成一个单向链表,vm_next指向下一个vm_area_struct结构体。链表的首地址由mm_struct中成员项mmap指出。
vm_ops是指向vm_operations_struct结构体的指针。该结构体中包含着指向各种操作的函数的指针。
struct list_head
{
struct list_head *next, *prev;
};
shared中的前后向指针,把有关的vm_area_struct结合成一个共享内存时使用的双向链表。
mm中的vma按地址排序由线性链表连接起来,当vma的数量相当大的时候启用AVL树,与线性链表同时管理vma以提高访问效率。随着vma的动态改变,vma之间存在归并和拆分等操作。分配vma并不立即分配页帧。
图4-4 进程虚存空间管理
所有vm_area_struct结构体组成一个红黑树,也称为AVL(Adelson-Velskii and Landis)树。AVL树是一种具有平衡结构的二叉树。
struct rb_node
{
struct rb_node *rb_parent;
int rb_color;
#define RB_RED 0
#define RB_BLACK 1
struct rb_node *rb_right;
struct rb_node *rb_left;
};
struct rb_root
{
struct rb_node *rb_node;
};
进程虚存空间如图4-4所示。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。