操作系统中分页管理内核的模拟与实现

本文探讨了现代操作系统采用的分页内存管理方式,以Intel 80386处理器为例,介绍了如何模拟设计和实现一个操作系统的分页管理内核。通过EmuHAL层提供对内存的原子操作模拟,包括读写服务集,同时讲解了分页地址、线性地址和虚拟地址的区别。代码示例展示了内存读写函数的实现。
摘要由CSDN通过智能技术生成

     操作系统中分页管理内核的模拟与实现

  

 现代操作系统大部分都采用了分页的方式对物理内存进行管理,Intel 从80386之后也在硬件上支持的分页管理,为操作系统的设计和实现带来了很多便利之处。
 由于实际去实现一个操作系统的内存管理内核是一件相对比较困难的事情,因此我们今天将用模拟的方式来设计和实现一个操作系统的分页管理内核。由于我自己也是一个学生,因此有说的不妥甚至错误的地方,请各位看官不要吝啬您的意见建议甚至批评。如无特别说明,下文的物理内存地址一词均指模拟物理内存中的物理地址。
一切开始以前先来确定一些基准数据:
1. 物理页的大小:物理页的大小通常和硬件系统有关,

2. 比如Intel80386及以后的处理器都是按4KB大小来分页。这里我们也选择用4KB来作为分页的尺寸。
3. 逻辑页的大小:逻辑页的大小个人认为和物理页取同4. 样大小最好,5. 因为这样可以不6. 用担心页边界问题,7. 并且在设计分页变换机构时比较容易实现。因此也取为4KB。
8. 系统能有效管理的最大内存:作为一个模拟系统,9. 这里我取32MB作为最大内存管理上限,10. 太大了没什么实际意义,11. 再说模拟系统跑起来也会很占资源。
12. 页号地址范围:依照逻辑页尺寸和支持的最大内存,13. 系统中最多可有8192个逻辑页面,14. 则页面地址为0x0-0x1FFF
15. 页内偏移量范围:页尺寸取为4KB 则页内偏移地址为0x0-0x0fff.。
在分页管理系统中,地址的通常不止一种。在我们现在要做的系统中,地址分为3类,既分页地址,线性地址,虚拟地址。
线性地址:线性地址既存储器的物理地址,在系统内核未初始化和初始化时,都将使用线性地址操作内存。
虚拟地址:虚拟地址是在分页后产生的。其含义和线形地址相同,在系统中存在的各个进程,都使用虚拟地址访问内存,虚拟地址传入内核后,内核利用该进程的页表和自身的页地址变换机构将虚拟地址映射为某一个线性地址。
分页地址:分页地址由2部分组成,按照上文的基准数据设定,其结构如下:
    BBBBBBBBBBBBB:BBBBBBBBBBBB
 前一部分共13bit用来记录页号地址,后一部分共12bit用来记录页内偏移量。由于我们将物理页和逻辑页的尺寸统一了,因此分页地址所表示的整体意义和线性地址是一样的。
例如:分页地址0000000000001000000000001 是0x1001。作为分页地址,它表示物理内存中第二个物理页(注意,不是第一个物理页,因为0x0000也是有效地址,表示的是第一个物理页),页内偏移量为01。这个分页地址所表示的线性地址为:页号×页尺寸+页内偏移量,即:1×4096+1=4097 (0x1001)。可以看到,从整体上来看,分页地址和线性地址在意义上一样的,但是分页地址和线性地址所表示的自身含义则是完全不同的,这一点在逻辑页和物理页尺寸不统一的情况下尤为明显,并且在这种情况下,分页地址和线性地址的整体含义也就完全不同了。由此看出,逻辑页和物理页尺寸的统一给我们设计和实现内核的地址变换部分提供了很大的便利。
 在正式开始设计内核前,先来看看现代操作系统的一个重要特征:硬件无关性。这个特征通常是提供给用户的。但是个人认为,在操作系统的底层设计中,也应该有一个接口部分是直接操作硬件,而其他部分在这个部分之上,通过这个接口去访问和控制硬件。这样做可以使得操作系统的实现可以在某种程度上脱离具体硬件规格,并且可以给移植带来方便。这种设计思路在Microsoft Windows NT家族的中得到了具体的体现,NT中使用了“硬件抽象层”(HAL)的概念来分离操作系统的相对高层部分和底层硬件。各个高层部分通过HAL层来访问和控制硬件,而不是将各种对硬件的访问和控制操作分散在系统的各个角落。这种设计思路将在我们今天即将开始设计的模拟内核中再一次得到体现。这就是“模拟硬件抽象层”(EmuHAL)。
 EmuHAL的设计思路完全取自Windows NT的HAL。作为给这个模拟内核搭配的HAL层,EmuHAL将提供一个基本上完全真实的物理内存环境供我们来操作,你可以认为通过EmuHAL层进行操作和访问的是完全的一个物理硬件,而不必要将其想象成别的什么。在这里,EmuHAL层通过利用一个数组,来提供真实的物理内存操作环境。下面是其提供的接口函数:

DWORD  APIENTRY Fn_HAL_Init(LPDWORD  lpMemoary,    
UDWORD   NewMemoaryLength);

BYTE APIENTRY Fn_ReadByte(UDWORD Address);
VOID APIENTRY Fn_WriteByte(UDWORD Address,DWORD Value);

WORD APIENTRY Fn_ReadWord(UDWORD Address);
VOID APIENTRY Fn_WriteWord(UDWORD Address,DWORD Value);

DWORD APIENTRY Fn_ReadDword(UDWORD Address);
VOID APIENTRY Fn_WriteDword(UDWORD Address,DWORD Value);

这些函数都是对内存的原子操作的模拟。除去第一初始化函数外,从上到下一共3组,每组对应一个读写服务集,分别为:字节读/写集,字读/写集, 双字读/写集。EmuHAL层的实现源代码如下:
宏定义头文件::Base.h
//=================================================================================
//Name:  Base.h
//Funcation: Base Type Define,CPU support,MyUnicode Support.
//Author: kakashi.R
//Last Update: 6.1.2005
//================================================================================= 


#ifndef _BASE_H
#define _BASE_H

#define  TRUE  1
#define  FALSE  0
#define  SUCCESS  1
#define  FAILURE  0
#define  ENABLE  1
#define  DISABLE  0
#define  HIGH  1
#define  LOW  0
#define  USED  1
#define  UNUSE  0
#define  NULL  0

#pragma message("")
#pragma message("This is A Header File Created by kakashir. :)")
#pragma message("")

#if (defined _IOSTREAM)   //Incude iostream.h for I/O.
#pragma message("")
#pragma message("Macro _IOSTREAM actived Now.")
#pragma message("Now iostream.h included for I/O.")
#pragma message("")
#include <iostream.h>
#endif  //#if (defined _IOSTREAM)


#if ( !(defined APIENTRY) && (defined _STDAPI) ) //__stdcall style support.
#pragma message("")
#pragma message("Macro _STDAPI actived Now.")
#pragma message("Funcations call style KeyWord APIENTRY Defined as __stdcall.")
#pragma message("")
#define  APIENTRY   __stdcall   
#define  CENTRY    __cdecl    
#define  API    APIENTRY   
#define  PASCAL    API    
#define  CALLBACK   PASCAL    
#endif  //#if ( !(defined APIENTRY) && (defined _STDAPI) )

#ifdef _I386   //Intel 80386 and later CPU support and typedefs.
#pragma message("")
#pragma message("Macro _I386 actived NOW.")
#pragma message("Data define Keywords defined for Intel 80386 and later Processer Family.")
#pragma message("")

#if !(defined _UNICODE)
typedef  char    CHAR;
#endif  //#if !(defined _UNICODE )

typedef  __int8    BYTE;
typedef  __int16    WORD;
typedef  __int32    DWORD;
typedef  __int64    QWORD;
typedef  float    FLOAT;
typedef  double    DOUBLE;

typedef  unsigned __int8  UBYTE;
typedef  unsigned __int16  UWORD;
typedef  unsigned __int32  UDWORD;
typedef  unsigned __int64  UQWORD;

typedef  void*    LPVOID;
typedef  WORD*    LPWORD;
typedef  BYTE*    LPBYTE;
typedef  DWORD*    LPDWORD;
typedef  QWORD*    LPQWORD;
typedef  FLOAT*    LPFLOAT;
typedef  DOUBLE*    LPDOUBLE;

typedef  UBYTE*    LPUBYTE;
typedef  UWORD*    LPUWORD;
typedef  UDWORD*    LPUDWORD;
typedef  UQWORD*    LPUQWORD;
    
typedef  bool    BOOL;
typedef  void    VOID;

typedef union ___uint128     //128bit unsigned data support.
{
 unsigned __int64  __128[2];
 unsigned __int32  __32[4];
 unsigned __int16  __16[8];
 unsigned __int8   __8[16];
}uint128;

typedef union ___sint128    //128bit signed data support.
{
 signed __int64  __128[2];
 signed __int32  __32[4];
 signed __int16  __16[8];
 signed __int8  __8[16];
}sint128;

typedef  UBYTE    U8;
typedef  short unsigned  U16;
typedef  long unsigned  U32;
typedef  UQWORD    U64;
typedef  uint128    U128;

typedef  BYTE    S8;
typedef  short signed   S16;
typedef  long signed   S32;
typedef  QWORD    S64;
typedef  sint128    S128;
#endif  //#ifdef  _I386


#if (defined _UNICODE && defined _I386)  // MyUNICODE support!! :)
#pragma message("")
#pragma message("Macro _UNICODE actived NOW.")
#pragma message("The Keyword CHAR(upcase) defined 16bit for support UNICODE now.")
#pragma message("Pleas use cout() to display UNICODE char or String.")
#pragma message("use __char to define Old ANSCII-Style Character :)")
#pragma message("")

typedef char __char;

typedef union ___wchar   //Define MyUNICODE struct.

 U16 Unicode16;
 U8 Unicode8[2];
}__wchar;

typedef   __wchar CHAR;

#include <UnicodeTable.h>  //include MyUNICODE Table :)
#define __IOSTREAM
#include <iostream.h>

#if (defined _IOSTREAM)

void APIENTRY _cout(CHAR Word)  //MyUnicode I/O Funcation for character Display
{
 if(Word.Unicode8[1] == 0)
  {
   cout<<(__char)Word.Unicode8[0]<<flush;
  }
 else
  {
   if(Word.Unicode8[1] >=0 && Word.Unicode8[1] <= HIGH_WORD_MAX &&
      Word.Unicode8[0] >=0 && Word.Unicode8[0] <= LOW_WORD_MAX)
    {
     _Buffer[0] = *(UnicodeTable[(Word.Unicode8[1]-1)] +   (2*Word.Unicode8[0])   );
     _Buffer[1] = *(UnicodeTable[(Word.Unicode8[1]-1)] +   (2*Word.Unicode8[0])+1 );
     _Buffer[2] = '/0';
     cout<<_Buffer<<flush;
    }
  }
 
}
void APIENTRY _cout(CHAR* Word)  //MyUnicode I/O Funcation for String display
{
 U32 Length = sizeof(Word)/2;
 U32 Tmp_a=0;
 for(Tmp_a=0;Tmp_a<Length;Tmp_a++)
  {
   if((Word+Tmp_a)->Unicode8[1] == 0)
    {
     cout<<(__char)( (Word+Tmp_a)->Unicode8[0])<<flush;
    }
   else if ( ((Word+Tmp_a)->Unicode8[1]) >=0 && ((Word+Tmp_a)->Unicode8[1]) <= HIGH_WORD_MAX &&
             ((Word+Tmp_a)->Unicode8[0]) >=0 && ((Word+Tmp_a)->Unicode8[0]) <= LOW_WORD_MAX )
     {
      _Buffer[0] = *(UnicodeTable[ ( (Word+Tmp_a)->Unicode8[1] )-1 ] + 2*( (Word+Tmp_a)->Unicode8[0] )  );
      _Buffer[1] = *(UnicodeTable[ ( (Word+Tmp_a)->Unicode8[1] )-1 ] + 2*( (Word+Tmp_a)->Unicode8[0] )+1);
      _Buffer[2] = '/0';
      cout<<_Buffer<<flush;     
     }
  }
}
#define  lpUnicode(var,value)  ((var)->Unicode16) = value
#define  Unicode(var,value)  ((var).Unicode16)  = value
#define  cout(UnicodeString)  _cout(UnicodeString) 

#endif  //#if (defined _IOSTREAM) 

#endif //#if (define __UNICODE)

#endif   //#ifndef _BASE_H
//===================================================================//
EmuHAL层源代码头文件 Hal_Define.h
#ifndef _HAL_DEFINE_H
#define _HAL_DEFINE_H


#define  _STDAPI
#define  _I386
#include <base.h>

#define  MAX_MEMOARY_SUPPORT 0x02000000

DWORD APIENTRY Fn_HAL_Init(LPDWORD lpMemoary,UDWORD NewMemoaryLength);

BYTE APIENTRY Fn_ReadByte(UDWORD Address);
VOID APIENTRY Fn_WriteByte(UDWORD Address,DWORD Value);

WORD APIENTRY Fn_ReadWord(UDWORD Address);
VOID APIENTRY Fn_WriteWord(UDWORD Address,DWORD Value);

DWORD APIENTRY Fn_ReadDword(UDWORD Address);
VOID APIENTRY Fn_WriteDword(UDWORD Address,DWORD Value);

#endif //#ifndef _HAL_DEFINE_H
//===================================================================//
EmuHAL 层源代码实现文件:Hal.cpp
#ifndef _HAL_H
#define _HAL_H
#include "../include/HAL_Define.h"


static LPDWORD lpMemoary=NULL;
static UDWORD MemoarySize=NULL;

DWORD APIENTRY Fn_HAL_Init(LPDWORD Init_lpMemoary,UDWORD NewMemoaryLength)
{
 DWORD Resault=SUCCESS;
 _asm
  {
   mov EAX,[Init_lpMemoary]
   cmp EAX,NULL
   je ERROR
   mov [lpMemoary],EAX
   jmp EXIT 
  ERROR:
   mov [Resault],FAILURE
  EXIT: 
   
  }
 MemoarySize = NewMemoaryLength;
 _asm
  {
   cmp [MemoarySize],MAX_MEMOARY_SUPPORT
   ja OverFlow
   jmp OK
  OverFlow:
   mov [MemoarySize],MAX_MEMOARY_SUPPORT
  OK:
  }
 return Resault;
}


BYTE APIENTRY Fn_ReadByte(UDWORD Address)
{
 BYTE Resault=NULL;
 _asm
  {
    push EBX
    
    mov EBX,[Address]
    mov EAX,[MemoarySize]
    cmp EBX,EAX
    jae EXIT
    
    mov EBX,[lpMemoary]
    add EBX,[Address]
    mov BL ,BYTE ptr[EBX]
    mov [Resault],BL
   EXIT:
    pop EBX
  }
 
 return Resault;
}

VOID APIENTRY Fn_WriteByte(UDWORD Address,DWORD Value)
{
 _asm
  {
    push EBX
    mov EBX,[Address]
    mov EAX,[MemoarySize]
    cmp EBX,EAX
    jae EXIT
    
    mov EBX,[lpMemoary]
    mov EAX,[Value]
    add EBX,[Address]
    mov BYTE ptr[EBX],AL
   EXIT:
    pop EBX
  }
}

WORD APIENTRY Fn_ReadWord(UDWORD Address)
{
 WORD Resault=NULL;
 _asm
  {
    push EBX
    
    mov EBX,[Address]
    mov EAX,[MemoarySize]
    sub EAX,0x02
    cmp EBX,EAX
    ja EXIT
    
    mov EBX,[lpMemoary]
    add EBX,[Address]
    mov BX ,WORD ptr[EBX]
    mov [Resault],BX
   EXIT:
    pop EBX
  }
 
 return Resault;
}

VOID APIENTRY Fn_WriteWord(UDWORD Address,DWORD Value)
{
 _asm
  {
    push EBX
    
    
    mov EBX,[Address]
    mov EAX,[MemoarySize]
    sub EAX,0x02
    cmp EBX,EAX
    ja EXIT
    
    mov EBX,[lpMemoary]
    mov EAX,[Value]
    add EBX,[Address]
    mov WORD ptr[EBX],AX
  
   EXIT:
    
    pop EBX
  }

}

DWORD APIENTRY Fn_ReadDword(UDWORD Address)
{
 DWORD Resault=NULL;
 _asm
  {
    push EBX
    
    mov EBX,[Address]
    mov EAX,[MemoarySize]
    sub EAX,0x04
    cmp EBX,EAX
    ja EXIT
    
    mov EBX,[lpMemoary]
    add EBX,[Address]
    mov EBX ,DWORD ptr[EBX]
    mov [Resault],EBX
   EXIT:
    pop EBX
  }
 
 return Resault;
}

VOID APIENTRY Fn_WriteDword(UDWORD Address,DWORD Value)
{
 _asm
  {
    push EBX
    
    
    mov EBX,[Address]
    mov EAX,[MemoarySize]
    sub EAX,0x04
    cmp EBX,EAX
    ja EXIT
    
    mov EBX,[lpMemoary]
    mov EAX,[Value]
    add EBX,[Address]
    mov DWORD ptr[EBX],EAX
  
   EXIT:
    
    pop EBX
  }
  
 

}
#endif //#ifndef _HAL_H   

以上3个文件中,Base.h 是我个人给自己写的类型定义和宏定义文件。在后面设计实现内核时也会用到。从现在开始,我们正式进入内核的设计。
 首先,我们要实现的模拟内核是内存分页管理内核,但是由于分页管理时有可能出现页表过长的情况(这在页尺寸较小但需要管理的内存总量很大时尤其明显),这时我们采用的方法是将页表分组,即多级页表的方式。那么我们现在正在设计的模拟内核也需要实现这样的功能吗?我个人给出的答案是否。并不是因为我们管理的最大内存只有32MB所以不需要这样的功能,而是由我们所设计的模拟内核在一个真实的操作系统所处的位置导致的。个人认为,在真实的操作系统中,内存管理内核应该并不是一个单独整体,而是由一个层次结构组成。大致上如下:

       系统调用接口层
       页表管理层(高级)
       页表管理层(低级)
       HAL层

其中“页表管理层(高级)”部分实现对二级或者多级页表的管理,而页表管理层(低级)部分则实现对于一级页表中的地址映射,变换操作,同时由于该部分是紧挨HAL层的,因此对物理内存的访问也将通过该层传

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值