Skip to content

Memory Allocation

kamaboko edited this page Jan 16, 2023 · 19 revisions

メモリ管理

ページング方式。4KB単位の物理メモリを仮想メモリに割当てる。
したがって、物理メモリの管理と、仮想メモリの管理を行った上で、双方のマッピングを行う必要がある。

Physical Memory

基本的な考え方

連続したある程度の大きさの物理的なメモリ空間を、4KBの固定長(ページ単位)で区切る。
物理の4KBの固定長の単位ページをここでは物理メモリブロックと表現する。
物理メモリブロックを管理するための配列を用意し、1要素を1ページのフラグとして管理する。

物理メモリブロック管理用の構造体例

typedef struct P_MEMMAN{
    uint8_t tbl[PMALLOC_MAX_PAGE];   // <= ブロックの管理テーブル。1byteの管理情報を持つ(使用中かなどのフラグを持つ)
    uint32_t base_addr;              // <= 開始アドレス(物理アドレス)
} P_MEMMAN;

ここで注意しなければならないのは、base_addr のアドレスから base_addr + PMALLOC_MAX_PAGE * 4KB分の物理メモリがこの構造体で管理されることである。
特別に意図する場合を除けば、この領域は後に説明する仮想メモリに割り当てて使用するため、他のカーネル機能が使用する物理メモリ領域と被らない必要がある。
(base_addr + PMALLOC_MAX_PAGE * 4KBの領域には、pmallocにより割り当てられた用途以外で使用されるべきではない)
他のカーネル機能が仕様するメモリと被ってしまうと、マッピングにされた仮想メモリを通じてその領域が破壊されうる。     上記のような構造体を管理情報として、base_addrを0x0000とした場合は、以下のようなマッピングになる。

block 0 -> 0x0000 - 0x0fff
block 1 -> 0x1000 - 0x1fff
block 2 -> 0x2000 - 0x2fff
...
base_addr                                   higher
+--------------------------------------------+ 
|        |        |        |        |        |
| block0 | block1 | block2 | block3 |  ...   |
|  4KB   |  4KB   |  4KB   |  4KB   |  4KB   |
+--------------------------------------------+ 

1ブロックは4KBなので、ブロックのindexに4KB(0x1000)をかけることでそのブロックの先頭アドレスを求められる。
その先頭アドレスから4KB分がそのブロックになる。

物理メモリの新規確保(pmalloc)

  1. (空きブロックの確保時には割り込みを禁止したほうが良いかも?)
  2. 空きブロックを探索する、空きブロックの探索は単純に管理テーブルtblの先頭から使用されていない領域を探す。
  3. 見つけた空きブロックの管理情報に使用中のフラグを立てる
  4. 確保したブロックの物理アドレスを返す
  5. 必要なメモリの量に応じて上記1-3を繰り返す

実装例

void *pmalloc_4k(void){
    P_MEMMAN *memman = get_phy_memman();                           // 管理用構造体の取得 *1
    for(uint32_t i = 0; i < PMALLOC_MAX_PAGE; i++){
        if(memman->tbl[i] == 0){
            memman->tbl[i] = 1;
            return((void *)memman->base_addr + MEM_PAGE_SIZE * i);
        }
    }
    return 0;
}

物理メモリの解放(pfree)

  1. (空きブロックの解放時には割り込みを禁止したほうが良いかも?)
  2. 引数から解放対象の物理アドレスを取得し、そのアドレスからbase_addrを引き、4KBで割って物理ブロックのindexを求める
  3. 対象のブロックの管理情報から使用中のフラグをクリアする

実装例

void pfree(void *addr){
    P_MEMMAN *memman = get_phy_memman();                          // 管理用構造体の取得 *1
    uint32_t page = rounddown((uint32_t)addr, MEM_PAGE_SIZE);     // 4KB単位で切り捨てる
    uint32_t index = (page - memman->base_addr) / MEM_PAGE_SIZE;
    memman->tbl[index] = 0;
}

補足

*1 物理メモリ管理用構造体の取得について

物理メモリ管理構造体のアドレスは、ページングの状態に応じて物理アドレス・仮想アドレスを使い分ける必要がある。
この関数はページングの状態に応じて、返すメモリアドレスを動的に変化させる役割を持つ。

P_MEMMAN *get_phy_memman(void){
    if(get_paging_status()){
        return (P_MEMMAN *) PMALLOC_MAN_ADDR_V;
    }
    
    return (P_MEMMAN *) PMALLOC_MAN_ADDR_P;
}
さらなる補足

物理メモリ管理用構造体はページング有効化前に初期化される。
この場合、この構造体へは物理アドレスを用いて参照される。 後述する仮想メモリの確保においても、物理メモリの管理は引き続きこの構造体を使用して行われるため、ページング有効化後にもこの構造体の参照が可能である必要がある。
ページングが有効でない場合は物理アドレスでアクセス可能であるが、ページングが有効化されている場合、MMUによりアドレスはすべて仮想アドレスと解釈される。
したがって、ページング有効化前は物理アドレスで、有効化後は仮想アドレスで構造体への参照を行う。
また、ページング有効化前に、この構造体が存在するメモリ空間の物理アドレス(PMALLOC_MAN_ADDR_P)と、仮想アドレス(PMALLOC_MAN_ADDR_V)の関連付けを、カーネル空間のページテーブルに記述しておく必要がある。

Virtual Memory

Virtual Memoryの管理では、アドレス的に連続したメモリの動的確保機能を提供する。
vmalloc はある程度の大きさの連続した仮想メモリ空間(uroborosではエクステントと呼ぶ)を、要求サイズに応じて切り出す。
実装そのものは、K&Rライクな簡易malloc。

  • vmallocは連続した仮想メモリ空間「エクステント」を管理する
  • vmallocは要求されたサイズのメモリを「ブロック」として割り当てる
  • ブロックは、flags(割り当て状況を記録する)、addr(ブロックの開始アドレス)、size(ブロックのサイズ)を基本情報に持つ
  • ブロックの各要素は、addr順に双方向のリスト構造になっている
  • 要求されたサイズを割り当てるだけのメモリがエクステントに存在しない場合、pmallocを使用してエクステントを拡張する
  • エクステントの拡張上限はvmallocの初期化時に設定する
  • メモリ解放時には、解放対象の前後のブロックを確認し、空きであれば1つの空き領域としてまとめる
  • (エクステントの縮小はそのうち実装するつもり)
  • 確保する領域の開始アドレス及び、サイズはアラインメントされる(とりあえず今は16byte)
|block0 free,  addr:0x0000, size:0x100|
|block1 alloc, addr:0x0100, size:0x200|
|block2 free,  addr:0x0300, size:0x700|

ページング(32bit)

説明が長くなってしまったので、個別のページを作成しました。 ページング