二、线性内存管理
32位的内存空间是4G,除开些保留的、给硬件的、还有一些特殊用途的将,剩余的前2G分给应用程序,由不同的程序单独使用(用户态),后2G分给系统由所有程序共享(核心态)。因此需要建立两个管理列表。
系统内存管理顺序表由内核文件编译时决定存放位置,不用额外申请空间。在这里可以体现将内核文件编译成PE文件的好处。
自定义的线性空间列表:
开始地址 | 结束地址 | 大小 | 说明 | |
0 | 0x000FFFFF | 1M | 保留 | |
0x00100000 | 0x7FFFFFFF | 2G-1M | 用户空间 | |
0x80000000 | 0x803FFFFF | 4M | 自由空间 | |
0x80400000 | 0x80412FFF | 1.2M | 内核映像 | |
0x80413000 | 0x80417FFF | 16K | 系统栈 | |
0x80418000 | 0x80419FFF | 8K | 物理内存map | |
0x8041A000 | 0xB0000000 | 783M | 自由空间 | |
0xC0000000 | 0xC03FFFFF | 4M | 页目录、页表 |
代码由mem_sys_virtual_init,mem_virtual_alloc,mem_virtual_alloc三个函数完成。将内存分成若干块,每块标注属性,需要申请时找到合适的块,或者从大块中分割小块出来。释放时查找前后块的属性,都为空闲时合并。
//系统内存管理顺序表初化
void mem_sys_virtual_init(PMEMORY_BASIC_INFORMATION pmbi)
{
DWORD size=((PIMAGE_DOS_HEADER)0x80400000)->e_lfanew+0x80400000;
size=((PIMAGE_NT_HEADERS)size)->OptionalHeader.SizeOfImage;
//oskrnl映像,文件有几个段,初始化内处段。此处合并一段
pmbi[0].BaseAddress=(PVOID)0x80400000;
pmbi[0].AllocationBase=MbiSys[0].BaseAddress;
pmbi[0].RegionSize=size;
pmbi[0].AllocationProtect=PAGE_EXECUTE_WRITECOPY;
pmbi[0].State=MEM_COMMIT;
pmbi[0].Type=MEM_IMAGE;
//内核栈
pmbi[1].BaseAddress=MbiSys[0].BaseAddress+size;
pmbi[1].AllocationBase=MbiSys[1].BaseAddress;
pmbi[1].RegionSize=0x4000; //此值应与 osloader.asm 239行的值同步,16K内核栈
pmbi[1].AllocationProtect=0;
pmbi[1].State=MEM_IMAGE;
//物理内存map
pmbi[2].BaseAddress=MbiSys[1].BaseAddress+MbiSys[1].RegionSize;
pmbi[2].AllocationBase=MbiSys[2].BaseAddress;
pmbi[2].RegionSize=0x2000; //+8K物理内存map
pmbi[2].AllocationProtect=0;
pmbi[2].State=MEM_PRIVATE;
//自由空间
pmbi[3].BaseAddress=MbiSys[2].BaseAddress+MbiSys[2].RegionSize;
pmbi[3].AllocationBase=MbiSys[3].BaseAddress;
pmbi[3].RegionSize=0xB0000000-(DWORD)(MbiSys[3].BaseAddress);
pmbi[3].AllocationProtect=0;
pmbi[3].State=MEM_FREE;
//0xC0000000-0xC0400000 4m 空间用于内存分页
//保留,用于存放内存管理数据
pmbi[4].BaseAddress=(PVOID)0xC0000000;
pmbi[4].AllocationBase=MbiSys[4].BaseAddress;
pmbi[4].RegionSize=0x400000;
pmbi[4].AllocationProtect=0;
pmbi[4].State=MEM_RESERVE;
//用于结束
pmbi[5].BaseAddress=(DWORD)MbiSys;
pmbi[5].AllocationBase=1;
pmbi[5].RegionSize=0;
pmbi[5].AllocationProtect=0;
pmbi[5].State=MEM_RESERVE;
}
//申请线性空间,以4K为单位分段分配
void *mem_virtual_alloc(PMEMORY_BASIC_INFORMATION pMbiBase,LPVOID lpAddress,
DWORD numbytes,DWORD state,DWORD flProtec)
{
PMEMORY_BASIC_INFORMATION pmbi=pMbiBase;
PMEMORY_BASIC_INFORMATION rMbi=0;
DWORD count=0;
void* pmem=NULL;
_ALIGN(numbytes,4);
//遍历
do
{
//当前块是空闲的,并且当前块>=需要的
if((pmbi->State==MEM_FREE)&(pmbi->RegionSize>=numbytes))
{
//1、当前块首地址=需要,保存当前指针,不再变更
//2、当前块首地址 < 或>需要,保存当前指针,不再变更
if(pmbi->BaseAddress==lpAddress)
{//
rMbi=pmbi;
if(rMbi->RegionSize==numbytes)
{//当前块大小=需要
//直接修改属性,
rMbi->State=state;
//return rMbi->BaseAddress;
pmem=rMbi->BaseAddress;
goto funcover;
}
count=1;
}
else if(rMbi==NULL)
{
rMbi=pmbi;
count=2;
}
}
pmbi++;
}while ((DWORD)pmbi->AllocationBase!=1);
//上面循环完后,pmbi为最后一个块,rMbi为可分割的块
switch(count)
{
case 1: //当前块首地址=需要,并且当前块大小>需要
change2:
//rMbi之后的块后移一块,rMbi分成两块,返回前块,
while(pmbi != rMbi)
{
pmbi[1]=*pmbi;
pmbi--;
}
pmbi++;//此处pmbi=后块,rMbi为前块
//rMbi大小、属性改变,后块需改变大小 ,首地址
pmbi->BaseAddress=(void*)((DWORD)rMbi->BaseAddress+numbytes);
pmbi->AllocationBase=pmbi->BaseAddress;
pmbi->AllocationProtect=rMbi->AllocationProtect;
pmbi->RegionSize=rMbi->RegionSize-numbytes;
pmbi->State=rMbi->State;
rMbi->RegionSize=numbytes;
rMbi->State=state;
rMbi->AllocationProtect=flProtec;
//return rMbi->BaseAddress;
pmem=rMbi->BaseAddress;
goto funcover;
break;
case 2:
if(rMbi->BaseAddress>lpAddress)
{//2.1当前块首地址 >需要
//分成两块,返回前块,后移+新建一块
printf("rmbi>lp \n ");
goto change2;
}
else
{//2.2当前块首地址 <需要
if((DWORD)(rMbi->BaseAddress)+rMbi->RegionSize==(DWORD)(lpAddress)+numbytes)
{ //2.2.1、如果后块能满足需要,则分成两块,返回后块,后移+新建一块
while(pmbi != rMbi)
{
pmbi[1]=*pmbi;
pmbi--;
}
pmbi++;
//前块只需要改变大小 ,后块需将大小、首地址,属性改变
rMbi->RegionSize-=numbytes;
pmbi->RegionSize=numbytes;
pmbi->BaseAddress=lpAddress;
pmbi->AllocationBase=lpAddress;
pmbi->State=state;
pmbi->AllocationProtect=flProtec;
//return pmbi->BaseAddress;
pmem=pmbi->BaseAddress;
goto funcover;
}
else if((rMbi->BaseAddress<lpAddress)&((DWORD)rMbi->BaseAddress+rMbi->RegionSize>(DWORD)lpAddress+numbytes))
{//2.2.2 中间块能满足分成三块,返回中间块,后移+新建二块
while(pmbi!=rMbi)
{
pmbi[2]=pmbi[1];
pmbi--;
}
pmbi++;
//第二块,大小=state
pmbi->AllocationBase=lpAddress;
pmbi->BaseAddress=lpAddress;
pmbi->State=state;
pmbi->RegionSize=numbytes;
pmbi->AllocationProtect=flProtec;
//第三块
pmbi++;
pmbi->AllocationBase=(void*)((DWORD)lpAddress+numbytes);
pmbi->BaseAddress=pmbi->AllocationBase;
pmbi->State=MEM_FREE;
pmbi->RegionSize=(DWORD)rMbi->RegionSize-((DWORD)lpAddress- (DWORD)rMbi->BaseAddress)-numbytes;
pmbi->AllocationProtect=0;
//第一块,将大小变为
rMbi->RegionSize=(DWORD)lpAddress-(DWORD)rMbi->AllocationBase;
pmbi--;
//return pmbi->AllocationBase;
pmem=pmbi->BaseAddress;
goto funcover;
}
else
{//2.2.3 ,虽然需要的地址在当前,但大小无法满足,分成两块,返回前块,后移+新建一块
goto change2;
}
}
//case 2:
}//switch(count)
funcover:
print_farmat_msg("mem_virtual_alloc :%x,%x,%x\n",pMbiBase,numbytes,pmem);
return pmem;
}
void mem_vitrual_free(PMEMORY_BASIC_INFORMATION pmbi,LPVOID lpAddress)
{
PMEMORY_BASIC_INFORMATION currentMbi,pMbi=pmbi,prevmbi,nextmbi;
int movecount=0;
do
{
if (pMbi->BaseAddress==lpAddress)
{
currentMbi=pMbi;
currentMbi->State=MEM_FREE;
break;
}
}while((DWORD)pMbi->AllocationBase!=1);
if(currentMbi !=0)
{
//检查前后块,是否需要合并,并移动除无用的块
prevmbi=(currentMbi-1);
nextmbi=(currentMbi+1);
if(prevmbi->State==MEM_FREE)
{//从当前块开始前移1个位置,后面再看能不能合并后块,
prevmbi->RegionSize+=currentMbi->RegionSize;
movecount=1;
}
if(nextmbi->State==MEM_FREE)
{
if (movecount==1)
{//前块+后块都可以合并
prevmbi->RegionSize+=nextmbi->RegionSize;
movecount=2;
}
else
{//只有后块可合并
currentMbi->RegionSize+=nextmbi->RegionSize;
currentMbi=nextmbi;
movecount=1;
}
}
// case 0: //不能向前也不能向后合并
// case 1,2://可能向前也可能向后
nextmbi=(currentMbi+movecount);
do
{
*currentMbi=*nextmbi;
currentMbi++;
nextmbi++;
}while((PDWORD)nextmbi->AllocationBase!=1);
}
}