简述
现代意义上的操作系统,就必须提供对内核的保护、对不同用户程序之间的隔离,并允许软件的装入位置浮动。提供这些功能,需要由单独的内存管理来支持。
上图ALU将程序中的地址计算转换为虚拟地址(线性地址),然后通过MMU将虚拟地址转换为实际的物理地址。这些过程对于应用程序而言是透明的。虚拟地址通过段选择子内容结合偏移地址得到,虚拟地址通过MMU映射机制,通常以页为单位进行映射,建立对应关系,最终访问物理内存页面。
由于内存管理涉及的细节很多,这里只记录重要的和书中需要细思理解的部分,具体内容还是间书籍,结合着看,应该就能理解。
内核对用户空间的管理
每个进程都有自己的用户空间,其进程控制块EPROCESS中有个指针VadRoot,指向代表用户空间的数据结构。定义为:
每个进程都存在一个这样的数据结构。其中 MemoryAreaRoot所指向一颗二叉树,MEMORY_AREA结构为:
typedef struct _MEMORY_AREA
{
PVOID StartingAddress;
PVOID EndingAddress;
struct _MEMORY_AREA *Parent;
struct _MEMORY_AREA *LeftChild;
struct _MEMORY_AREA *RightChild;
ULONG Type;
ULONG Protect;
ULONG Flags;
BOOLEAN DeleteInProgress;
ULONG PageOpCount;
union
{
struct
{
ROS_SECTION_OBJECT* Section;
ULONG ViewOffset;
PMM_SECTION_SEGMENT Segment;
BOOLEAN WriteCopyView;
LIST_ENTRY RegionListHead;
} SectionData;
struct
{
LIST_ENTRY RegionListHead;
} VirtualMemoryData;
} Data;
} MEMORY_AREA, *PMEMORY_AREA;
当需要从一个空间中分配一个地址区间时,可以通过MMCreateMemoryArea()创建一个MEMERY_AREA节点,且插入其二叉树中。上面结构成分Data是个union,如果这个区间代表一个Section,即文件映射区或共享内存区,Data就是一个SectionData数据,如果代表普通已分配内存,则为VirtualMemoryData数据结构,其RegionListHead用来维持一个区块队列,一个已分配的区间只是一个地址连续的范围,不一定具有同类型保护模式,而区块则不但地址连续,而且具有相同类型保护模式,一个区间可能具有多个区块。区块结构定义如下:
typedef struct _MM_REGION
{
ULONG Type;
ULONG Protect;
ULONG Length;
LIST_ENTRY RegionListEntry;
} MM_REGION, *PMM_REGION;
区块是由一组虚存页面构成,具有相同保护模式。所以对于内存管理有这样层次:空间,区间,区块,页面。
宏操作#define CONTAINING_RECORD(address,type,field) ((type FAR*)(\
(PCHAR)(address) - (PCHAR)(&((type *)0)->field)))
宏展开为:
((MM_REGION FAR *)((PCHAR)(current_entry) - (PCHAR)(&((MM_REGION *)0)->RegionListEntry)))
在current_entry的基础上减去RegionListEntry在MM_REGION结构内部的位移。