|
0から作るOS開発 物理メモリ管理その2 物理メモリ管理
|
前回までの内容
これまでで、
- メモリの種類にROM、RAMがありROMにはEEPROM、フラッシュメモリ、RAMにはDRAM、SRAMがある
- CPUがDDRコントローラを通してDRAMを制御している
- BIOSからメモリ情報を取得するにはINT 0x15命令を使用する
- INT 0x15命令の0xE820機能でメモリマップ情報が取得できる
- メモリマップ情報はマルチブート仕様に従ってカーネルに渡す
ことがわかりました。それではカーネルを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 );
}
-
system_memory_size:BIOSのINT 0x15命令で取得したメモリサイズを入れます
-
system_memory_blocks:メモリサイズをメモリブロックサイズ4096バイトで割った値をいれます
-
free_blocks:フリーブロック数を入れます
-
memory_map:カーネルの直ぐ後のアドレスをビットマップ領域として使用します
-
memory_map_size:ビットマップのサイズを入れます。ビットマップサイズは全ブロック数÷32(intのサイズ)です
-
初期化時にはビットマップを全て1にセットして全メモリブロックを割り当て済みとしています
ビットマップを操作する
メモリ領域を割り当てるときにそのメモリブロックに該当するビットマップを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単位の物理メモリブロックの割り当てと解放ができるようになりました
次回はページングについて説明します