0から作るソフトウェア開発

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

0から作るOS開発 物理メモリ管理その2 物理メモリ管理

前回までの内容

これまでで、 ことがわかりました。それではカーネルを0から開発していきましょう!

今回は物理メモリ管理について説明します

物理メモリ管理

ここまでで、マルチブート仕様に従ってカーネルが使用できるメモリマップ情報を取得することができました

ここからが本題の物理メモリ管理になります

メモリ管理

メモリ管理と難しく言いましても、実際にはどの領域が使用されているのか、どの領域がフリーなのかを

プログラム上で憶えておくだけです。一口で言うとかなり簡単かなぁと思いますが実際やってみると

とてつもなく難しいです。現在のCPUアーキテクチャですと、OSの大分部がメモリ管理に頭を悩ませ、

労力を投入するはめになりますが、仕方ありません。それがOSの仕事と割り切るしかありません。。。

メモリ管理の方法

メモリの各バイトについて、使われているかどうかを記憶していては、

記憶するだけでメモリをものすごく使ってしまいます

例えばパソコンに4GBの物理メモリを載っているとすると、

合計4294967296バイトととてつもない数字になります

これだけのバイト数をそれぞれ使っているかどうか記憶するは大変です

そこで、メモリをある一定のブロックにわけて

管理していきます。ブロックのサイズをどの程度にするかは色々な手法があるかと思います

このサイトでは物理メモリを管理する1つのブロックのサイズは4KBとしています

パソコではIntelのプロセッサが主に使われていて、そのCPUでは仮想記憶のページングも

もちろんサポートしています

ページングでは仮想記憶領域をページという固定サイズのブロックで分割して管理しますが

このサイズが4KB(4MBにもできます)となっています

仮想メモリ管理と物理メモリ管理で同じブロックサイズに

しておくと、ソフトウェアがより簡単にできますので、同じ4K単位で管理していきます

たとえ1バイトのメモリを割り当てたいところでも、メモリの割り当ての最低サイズは4KBとなります



ここでは、メモリを使用しているのか、フリーなのかを記憶するのに

一番簡単なビットマップで管理していきます

ビットマップでメモリを管理する

ビットマップは単純にビットが1であれば、そのメモリ領域は使用していて

ビットが0であればフリー領域と決めておきます。(逆でもいいです)

そして、1ビットが4KBのブロックを表しています

ビットマップによる物理メモリ管理方法

この4KBを表すビットをメモリサイズ分だけ用意しておきます

例えば512MBの物理メモリを管理するビットマップのためのメモリサイズは

	

物理メモリ管理に必要なビットマップのサイズ
        = ( 512 × 1024 × 1024  バイト ) / ( 4 × 1024  バイト  ) / 8 ビット
        = 16384バイト



となります。16KBと結構大きいです。これをC言語のグローバル変数で

16KB分確保してもいいのですが、それだと異なる物理メモリサイズに対応することが

できません。このように考えていくと、物理メモリを管理するための大きなメモリ領域をいったい

どのように確保すればよいのか結構悩みどころです

カーネルのイメージには置くことは難しいので、ポインタを使用していこうかと思います

カーネルにはポインタだけ持たせておいて、起動後にBIOSのメモリマップ情報を取得して

そのメモリサイズ分を管理できるだけのビットマップ領域を確保します。確保した領域の

アドレスをポインタに格納しておけば後は自由に使うことができます

では、どのに領域を確保すればよいのか?ということでまた悩んでしまいます

カーネルイメージが置かれた直ぐ後には広大なメモリ領域が広がっているので

そこを利用していきます

カーネルイメージの直ぐ後にビットマップの領域を確保します

ビットマップに使用するメモリ領域を確保する

カーネルのサイズを取得します

	

/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :getSizeOfKernel
    Input       :void
    Output      :void
    Return      :unsigned int
    Description :return size of kernel image
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC INLINE unsigned int getSizeOfKernel( VOID )
{
    return( _BSS_END - _TEXT_START);
}



リンカエディタでセクションの設定した通りに、カーネルイメージのセクションは

作られています。ですので、BSSセクションの最終アドレスからテキストセクションを引くと

カーネルイメージのサイズが取得できます

ここでキーワードを追加しています

PUBLICキーワードとPRIVATEキーワードとVOIDキーワードとINLINEキーワードです

これらは特に意味はありませんので、適当に読み替えていただけますと助かります

PUBLICキーワードは外部参照可能な変数・関数につけていきます

PRIVATEキーワードは内部参照のみ可能な変数・関数につけていきます

	

/*
==================================================================================

	Description : Keyword Definition

==================================================================================
*/
#define	PUBLIC
#define	PRIVATE             static
#define	INLINE              __attribute__( ( always_inline ) )

typedef	void                VOID;
typedef _Bool               BOOL;
typedef int                 STATUS;
typedef unsigned char       UINT8;
typedef signed char         INT8;
typedef unsigned short      UINT16;
typedef signed short        INT16;
typedef unsigned int        UINT32;
typedef signed int          INT32;
typedef unsigned long long  UINT64
typedef signed long long    INT64

#define Null                0x00000000
#define	True                ( 0 == 0 )
#define	False               ( 0 == 1 )



と定義しました

__attribute__はGCCコンパイラキーワードです。INLINEで宣言した関数は

インライン関数(その関数を呼び出した場所に命令が展開される:挿入される)として宣言されます

ビットマップを定義するため、物理メモリ管理情報を宣言します

	

/*
==================================================================================

    Description : Physical Memory Management

==================================================================================
*/
typedef	struct
{
    unsigned int    system_memory_size;      /* システムのメモリサイズ     */
    unsigned int    system_memory_blocks;    /* システムのメモリブロック数 */
    unsigned int    allocated_blocks;        /* 割り当て済みブロック数     */
    unsigned int    free_blocks;             /* フリーブロック数           */
    unsigned int*   memory_map;              /* ビットマップ               */
    unsigned int    memory_map_size;         /* ビットマップのサイズ       */
} PHYSICAL_MEMORY_INFO;

/*
==================================================================================

    Management 

==================================================================================
*/
PRIVATE	PHYSICAL_MEMORY_INFO	pm_info;	/* 物理メモリ管理情報	*/



カーネルは0x00010000にロードされるので、0x00010000足すカーネルのサイズが

ビットマップのアドレスとなります。物理メモリ管理の初期化で設定していきます

	

/*
==================================================================================

    Description : Memory

==================================================================================
*/
#define	DEF_MEMORY_KERNEL_START	0x00100000

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	
    < open functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :getSystemMemoryBlocks
    Input       :void
    Output      :void
    Return      :unsigned int
    Description :return maximum number of memory blocks
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC INLINE unsigned int getSystemMemoryBlocks( VOID )
{
    return( pm_info.system_memory_blocks );
}

/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :getSystemMemorySize
    Input       :void
    Output      :void
    Return      :unsigned int
    Description :return system memory size
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC INLINE unsigned int getSystemMemorySize( VOID )
{
    return( pm_info.system_memory_size );
}

/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :initPhysicalMemoryManagement
    Input       :unsigned int memory_size
                 < system memory size >
    Output      :void
    Return      :void
    Description :initialize physical memory management
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC VOID initPhysicalMemoryManagement( unsigned int memory_size )
{
    pm_info.system_memory_size   = memory_size;
    pm_info.system_memory_blocks = getSystemMemorySize( ) / DEF_MEMORY_BLOCK_SIZE;
    pm_info.allocated_blocks     = getSystemMemoryBlocks( );
    pm_info.free_blocks          = 0;
    pm_info.memory_map
            = ( unsigned int *)( DEF_MEMORY_KERNEL_START + getSizeOfKernel( ) );
    pm_info.memory_map_size		= getSystemMemoryBlocks( ) / 32;

    /* ------------------------------------------------------------------------ */
    /*  all blocks are allocated at initialization                              */
    /* ------------------------------------------------------------------------ */
    kmemset( ( VOID* )pm_info.memory_map, 0xFF, pm_info.memory_map_size );
}



ビットマップを操作する

メモリ領域を割り当てるときにそのメモリブロックに該当するビットマップを1にし、

解放するときに0にするため、ビットマップを操作する関数を用意します

ビットマップに1をセットする

memory_mapを配列として扱います。セットしたいビット番号をbit_numberとし関数を

呼出すことで目的のビットに1をセットします。例えばsetBit( 64 );と呼出すとビットマップの

64番目のビットを1にします


/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    < Local Functions >

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
/*
==================================================================================
    Funtion     :setBit
    Input       :int bit_number
                 < bit number to set >
    Output      :void
    Return      :void
    Description :set bit of bit_number
==================================================================================
*/
PRIVATE INLINE VOID setBit( int bit_number )
{
    pm_info.memory_map[ bit_number / 32 ] |= ( 1 << ( bit_number % 32 ) );
}



ビットマップをクリアする

セットする関数とは逆に目的のビット番号のビットをANDでクリアします

					

/*
==================================================================================
    Funtion     :clearBit
    Input       :int bit_number
                 < bit number to clear >
    Output      :void
    Return      :void
    Description :clear bit of bit_number
==================================================================================
*/
PRIVATE INLINE VOID clearBit( int bit_number )
{
    pm_info.memory_map[ bit_number / 32 ] & = ~( 1 << ( bit_number % 32 ) );
}



ビットマップがセットされているか調べる

ビットマップが1か0かを判定する関数です

該当ビットがセットされていれば0より大きくなっているので、0と比較した結果(BOOL型)を返します

					

/*
==================================================================================
    Funtion     :testBit
    Input       :int bit_number
                 < bit number to test >
    Output      :void
    Return      :BOOL
                 < True >
                 bit is set
                 < False >
                 bit is cleared
    Description :test bit of bit_number
==================================================================================
*/
PRIVATE INLINE BOOL testBit( int bit_number )
{
    unsigned int	_test;

    _test = pm_info.memory_map[ bit_number / 32 ] & ( 1 << ( bit_number % 32 ) );

    return( _test > 0 );
}



割り当て可能なメモリブロックをビットマップから探す

メモリブロックを使用済み(割り当てる)にするには、まずフリーなメモリブロックが

どこにあるのかを知っておく必要があります。そのため、ビットマップの0番目のビットから

ビットが0になっているビットを探しだします。
					

/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :findFirstFreeMemoryBlock
    Input       :void
    Output      :unsigned int *block_number
                 < free block number ( index of bitmap ) >
    Return      :STATUS
                 < status >
    Description :find a first free memory block
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC STATUS findFirstFreeMemoryBlock( unsigned int *block_number )
{
    unsigned int    bitmap_index;
    unsigned int    bit_count;
    unsigned int    found;

    /* ------------------------------------------------------------------------ */
    /*  iterate each element of the bitmap                                      */
    /* ------------------------------------------------------------------------ */
    for( bitmap_index = 0 ;
         bitmap_index < pm_info.memory_map_size ;
         bitmap_index++ )
    {
        /* -------------------------------------------------------------------- */
        /*  if the element has candidate spaces                                 */
        /* -------------------------------------------------------------------- */
        if( pm_info.memory_map[ bitmap_count ] != 0xFFFFFFFF )
        {
            /* ---------------------------------------------------------------- */
            /*  iterate each bit of the element                                 */
            /* ---------------------------------------------------------------- */
            for( bit_count = 0 ; bit_count < 32 ; bit_count++ )
            {
                if( False == testBit( bit_count ) )
                {
                    /* -------------------------------------------------------- */
                    /*  return bitmap index number                              */
                    /* -------------------------------------------------------- */
                    *block_number
                        = bitmap_index * sizeof( unsigned int ) * 8 + bit_count;

                    return( 1 );
                }
            }
        }
    }
    
    return( -1 );
}



一番外側のfor文でビットマップの1つ1つの要素を繰り返し見ていきます

まずは大雑把に見ていくために、0xFFFFFFFFと比較しています。

その要素のメモリが全部1だった場合は空きなしとなりますので、さっさと次の要素を見に行きます

1つでも空きがあれば(0xFFFFFFFFでなければ)その要素(unsigned int型)の32ビットのなかに

0であるビットがあるかどうかをtestBit関数の返り値で判断しています

空きがあれば、メモリブロックの番号(何番目のビットか)を計算して返します

物理メモリの割り当てと解放

物理メモリは4KB単位で割り当てたり解放したりします。メモリを割り当てたら、該当するビットマップを

1にセットし、開放したら、該当するビットマップを0にします

物理メモリ管理の初期化でビットマップの全てを1にセットし、全てのメモリブロックを割り当て済みとしました

BIOSのINT 0x15命令の0xE820機能で取得したメモリマップ情報をたよりに、

利用可能なメモリブロックを解放していきます


/*
==================================================================================
    Funtion     :initFreedMemoryRegion
    Input       :VOID *base_address
                 < base address of freed memory region >
                 unsigned int size
                 < size of freed memory region >
    Output      :void
    Return      :void
    Description :set bits of freed memory region
==================================================================================
*/
PRIVATE VOID initFreedMemoryRegion( VOID *base_address, UINT32 size )
{
    UINT32 block_number;
    INT32  block_size;
    INT32  i;

    block_number = ( UINT32 )base_address / DEF_MEMORY_BLOCK_SIZE;
    block_size   = size / DEF_MEMORY_BLOCK_SIZE;

    /* ------------------------------------------------------------------------ */
    /*  all blocks of the memory region are freed                               */
    /* ------------------------------------------------------------------------ */
    for( i = block_size ; 0 < i ; i-- )
    {
        clearBit( block_number );

        block_number--;
        pm_info.allocated_block--;
        pm_info.free_block++;
    }
}



また同様にメモリマップ情報で利用不可なメモリを割り当て済みとします


/*
==================================================================================
    Funtion     :initAllocatedMemoryRegion
    Input       :VOID *base_address
                 < base address of allocated memory region >
                 unsigned int size
                 < size of allocated memory region >
    Output      :void
    Return      :void
    Description :set bits of allocated memory region
==================================================================================
*/
PRIVATE VOID initAllocatedMemoryRegion( VOID *base_address, UINT32 size )
{
    UINT32 block_number;
    INT32  block_size;
    INT32  i;

    block_number = ( UINT32 )base_address / DEF_MEMORY_BLOCK_SIZE;
    block_size   = size / DEF_MEMORY_BLOCK_SIZE;

    /* ------------------------------------------------------------------------ */
    /*  all blocks of the memory region are allocated                           */
    /* ------------------------------------------------------------------------ */
    for( i = block_size ; 0 <= i ; i-- )
    {
        setBit( block_number );

        block_number--;
        pm_info.allocated_block++;
        pm_info.free_block--;
    }
}



メモリブロックの初期化が終わり、物理メモリを管理できる状態になったら、

必要に応じてメモリブロックを割り当てたり、解放したりします

割り当て関数はmalloc関数とfree関数と似たような入出力とします


/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :allocSingleMemoryBlock
    Input       :void
    Output      :void
    Return      :VOID*
                 < address of allocated memory block >
    Description :allocate a memory block
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC VOID *allocSingleMemoryBlock( VOID )
{
    unsigned int block_number;
    VOID         *physical_address;
    STATUS       status;

    /* ------------------------------------------------------------------------ */
    /*  there is no free block                                                  */
    /* ------------------------------------------------------------------------ */
    if( pm_info.free_blocks <= 0 )
    {
        return( Null );
    }
	
    /* ------------------------------------------------------------------------ */
    /*  get a free block number                                                 */
    /* ------------------------------------------------------------------------ */
    status = findFirstFreeMemoryBlock( &block_number );

    if( status != 1 )
    {
        return( Null );
    }
	
    setBit( block_number );

    physical_address = ( VOID* )( block_number * DEF_MEMORY_BLOCK_SIZE );

    pm_info.allocated_blocks++;
    pm_info.free_blocks--;

    return( physical_address );
}



物理メモリの解放をします


/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :freeSingleMemoryBlock
    Input       :VOID *physical_address
                 < address of deallocating memory block >
    Output      :void
    Return      :void
    Description :free a memory block
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC VOID freeSingleMemoryBlock( VOID *physical_address )
{
    unsigned int block_number;

    block_number = ( unsigned int )physical_address / DEF_MEMORY_BLOCK_SIZE;

    clearBit( block_number );

    pm_info.allocated_blocks--;
    pm_info.free_blocks++;
}



これで4KB単位の物理メモリブロックの割り当てと解放ができるようになりました

次回はページングについて説明します

inserted by FC2 system