3章へ || 5章へ || 目次へ

4 メモリマネジメント

   ここでは、legOSにおけるメモリマネージャの動作について調べてみる。
  legOSは組み込み型汎用8bitマイコンで動作し、 取り扱うメモリ空間も32Kbytesと小さく (アドレッシング空間は16bitなので65K)、 Pentiumなどの高機能なプロセッサのようにハードウェアーでサポートされた セグメント機構による保護機能やページング機構による仮想メモリ、 大容量ストレージのキャッシングなどの繁雑な処理と そのマネジメントアルゴリズムの実装の必要がないので メモリマネージャの機能はいたってシンプルである。 legOSでサポートすべきメモリマネージメント機能は 主にマルチタスキングでのタスク生成や消滅に伴うメモリイメージの管理である。

4.1 メモリ管理アルゴリズム

   カーネルがマルチプログラミングをサポートすると、 プロセスの生成や消滅にともなって動的にメモリを割り当てたり 開放したりする必要がある。 メモリマネージャはどこにどれだけの空きがあり、 どこがどのプロセスに使用されているかを把握し、 必要な時には必要なだけのメモリをプロセスに与えたり、 必要がなくなったときには空き領域資源として管理し、 将来の割り当てに備えなくてはならない。


legOSではメモリを不定長セグメント (i386以降のようなセグメント機構じゃなくてソフトで論理的に 分割した領域のこと!) に分割して管理する。 各セグメントは図3のようにヘッダ部分32bitをもつ 連続した領域であり、各々のヘッダ部分には使用しているプロセスID(16bit値) とそのセグメントのサイズ(16bit値)が格納される。 空き領域にはプロセスIDのかわりにMM_FREE(0x0000)が格納され、 そのセグメントが空き領域であることが示される。
  また、セグメントサイズはバイトでなくクリックで管理され、 legOSでは1クリックが2バイトのサイズを持つ。 メモリをバイトでなくクリックで管理するのは そのほうが一般的に効率がよいからである。 (PCのように仮想メモリなどで32bitのメモリ空間を管理する場合などでは 256バイトのクリックサイズだったとして、 1024GBまでのメモリ空間が管理参照できる。) legOSのような小さなメモリ空間でクリックで管理する必要があるか どうかはちょっと疑問ではあるがとにかくそうなっている。

   さらに、PCのような大規模なメモリ空間の管理には管理用の特別の メモリ領域を確保して空きセグメントリスト、使用セグメントリストを 設けて、メモリ管理はこのリストを参照して行われる。 (ギガバイト級のメモリを端っこから見てたら日がくれちゃうでしょ!) しかし、legOSでは管理すべき空間がそもそも狭いので、 メモリ空間そのものをリストとして管理参照している。

   カーネルがメモリ割り当てを行う際まず始めに行うことは、 必要なだけの連続した空き領域が存在するかを調べることである。 カーネルはmm_startで示されるメモリマネジメント領域の最初のアドレス からスタック領域の下限までの全てのメモリ空間から適切な空き領域 を選び出さなくてはならないが、legOSではこのアルゴリズムに ファーストフィット法を採用している。 以下に代表的な探索アルゴリズムをならべて眺めてみる。

ファーストフィット
もっとも単純なアルゴリズムである。 探索リストの最初から順番に探索し、十分な空き領域を見つけると そこを分割してプロセスに与え、残りを空き領域として記録する。 ファーストフィットはできるだけ検索量を少なくする働きがあるため 高速なアルゴリズムである。

ネクストフィット
ファーストフィットと同様な働きをするが、該当する空き領域を 検出したときに、その位置を記憶しており、次回起動されたときには そこから検索を開始するので毎回最初から検索するわけではない。 しかし、Bays(1977)によるシミュレートでは ファーストフィットよりもネクストフィットのほうが やや性能が劣っていたらしい。

ベストフィット
ベストフィットはリスト全体を検索し、該当する空き領域のなかで 最も小さいものを採用する。あとで必要となるかもしれない大きな 空き領域を分割することをせず、もっともフィットしたサイズの 空き領域を採用する。つまり検索時間をよけいにかけてでも メモリの使用効率を向上させようという試みである。 しかし実際には使い物にならないとても小さな領域を増やす結果 につながって使用効率はファーストフィットに劣るらしい。

ワーストフィット
ベストフィットの問題を回避しようとした方法で、 最も大きな領域を採用する。これもシミュレーションでは ファーストフィットに負けてしまったらしい。

クイックフィット
空き領域探索リストをサイズ毎に分類した別々のリストで管理する。 例えば2Kの空きリストと8Kの空きリストといったようにサイズごとに 設ければ必要な空き領域の探索が非常に高速化できる。 でも、legOSで複雑なリストを作るなんて限られたメモリ空間 (simple-rover.srecのダウンロード後は空き領域は全部で 11.5KBしかない!)の浪費でしょう?

というわけで legOSの採用しているアルゴリズムは賢明な選択だったのであーる。

4.2 メモリマネージャの初期化

   NO_MEMORY_MANAGEMENTマクロを未定義でコンパイルされたカーネルは 初期化シーケンスにおいてmm_init()の呼び出しでメモリマネージャを 初期化する。 初期化の手続きは以下。



・MM_BLOCK_FREE()マクロでmm_startで示されるアドレスを 最初のセグメントヘッダとして MM_FREE(0x0000)を書き込み、 空き領域であることを記録する。 unsigned mm_startはカーネルの終りをしめすシンボルとして メモリに格納されるようリンカに指示して作成されている。

・MM_BLOCK_RESERVED()マクロでその次のアドレスに 空きセグメントのサイズの初期値 0xc000-mm_start のクリック数を記録して 最初の大きな唯一の空きセグメントを作成する。 そして、空きセグメントの次のアドレスに MM_RESERVED(0xFFFF)を記入しスタック領域を保護し、 その領域のサイズとして 0x4000を記録する。 そうすることで 0xC000+0x4000=0x0となるので それ以降すべての領域が使用不可であることを記録する。

・次にこれからの空き領域探索に備えてmm_first_freeを mm_first_free = mm_start で初期化する。 mm_first_freeは文字通り最初の空き領域へのポインタであり、 空き領域探索はここからスタートする。

・最後にメモリマネジメント用のセマフォ mm_semaphoreを初期化する。 マルチタスキングでは malloc などをユーザプロセスが 行っているときにタスクスイッチが発生して、 より優先順位の高いタスクが malloc を呼び出すかもしれないのでセマフォを用いた クリティカルセクション(メモリリスト)の保護が必要である。


以上が初期化内容である。 初期化直後のメモリイメージを図4に示す。

4.3 malloc

   メモリの動的な確保の手続きを調べることでlegOSのメモリマネージャの働きが 全て分かるといっても過言ではないので調べてみることにする。 mallocは引数として確保すべき領域サイズ(bytes)を表す16bit値 size をとる。



・まず size = (size+1)>>1 としている。これで legOSがメモリサイズをバイトでなくクリックで管理していることが 分かる。

・sem_wait(&mm_semaphore)でメモリリストアクセスのセマフォを 取得しようとする。ここからの操作がクリティカルセクション である。セマフォが取得できなかった場合は呼び出したタスクは ブロックされ他のタスクに実行権が委ねられる。

・mm_first_freeで示されるリストの最初から、順番に検索して行く。 空き領域が見つかると、そのサイズを参照して 確保したいサイズsizeより大きいことを検証する。 そして条件にかなっているならば確保したプロセスのpid (cpid)をヘッダに書き込む。

・ヘッダのサイズ情報を書き込む前に、残りの空き領域を 空き領域として残す(リストに残す)かどうか判定する。 使い物にならない小さな空き領域を一人前にリストに エントリさせたままにしておくと検索時間が無駄になるからである。 legOSでは 10クリック(20bytes)以下の空き領域は 無視することにしている。

・空き領域として残すと判断した場合は mm_try_join()をコールする。 mm_try_join()は新しくできた空き領域の、 そのまた次の領域が 空きであるかどうかを見て、空き領域であった場合は 二つの空き領域を連結する手続きである。

・次に新しくプロセスに確保した領域が元の mm_first_freeな領域 であった場合は mm_update_first_free()をコールする。 mm_update_first_free()は文字通り、mm_first_freeが きちんと最初の空き領域を指すように変更する手続きである。

・最後に sem_post(&mm_semaphore)してアクセス権を放棄する。

・戻り値として、領域確保に成功した場合は確保した領域のポインタを、 失敗した場合はNULLを返す。


いま見て来たように、空き領域と使用領域とにかかわらずリスト(メモリ) の始めから順番に検索しているのがちょっと引っかかるけれど、 まあmm_first_freeで少しはマネージャさんの負担を軽減してるわけだし、 legOSのメモリマネジメントは必要にして十分、 簡潔明瞭かつ高速な実装になっていると言えるよね!
3章へ || 5章へ || 目次へ
Copyright(c)1999 JiroEto