このテキストは、運営している韓国のブログを日本語に翻訳したものです。

韓国のブログ:https://yunsuu.notion.site/e379eccd32c545419f0651f743c12c13?v=35310b282de24cc5a292fe45cc498a32&pvs=74

 

この記事は「Operating Systems: Three Easy Pieces」を読んでまとめた本です。

著者が無料で公開しているので、興味のある方はぜひ読んでみてください。

本にある良い問題は解いてgithubにアップロードしています。

github:https://github.com/yunsuu/ostep-homework-solution 

本のリンク:https://pages.cs.wisc.edu/~remzi/OSTEP/

以前の仮想メモリ方式の問題点

私たちが前の記事で学んだ仮想メモリ方式には問題があります。

それは、ヒープ部分での無駄が非常に多いということです。どれくらい動的に割り当てられるのかよくわからないため、余剰スペースを設けなければならず、これが無駄の主な原因でした。

どのようにして無駄になるヒープ領域を減らすことができるのでしょうか?

減らす方法は、各セグメンテーションごとにベースとバウンドの値を保存しておくことです。

(スタックはコードやヒープとは逆方向に増加することも忘れないで覚えておきましょう!)

次の図のように、どこからがヒープで、どこからがスタックであるかを明確に示します。このように分割された各部分をセグメントと呼びます。

表を元に分割されたメモリの様子を見ると、次のようになります。どの部分が有効で、どの部分が無効であるかを示すことができます。これで、C言語のプログラマーが最も嫌うsegment faultのエラーを理解することができます。プログラムが意図しないセグメントにアクセスしたときにsegment faultエラーメッセージが出てプロセスが終了します。

すべての問題が解決されたわけではありませんが、問題解決の手がかりが見え始めました。

セグメントの種類の把握

さて、セグメントを分割したので、これらを認識する方法も必要でしょう。

セグメントを認識する方法は、仮想メモリの最上部の数ビットを使用し、そこにどのセグメントであるか、セグメントの何番目を探しているかの情報が入っています。

セグメントセクションは、どのヒープであるか、スタックであるか、コードセグメントであるかを区別する場所で、オフセットセクションは、区別されたセグメントのどの部分であるかを示す部分です。現在の値は、ヒープ(01)セグメントの109(000001101000)番目を参照していることを示す図です。

このような方法もありますし、別の方法としては、ハードウェアで生成されたアドレスの出所によって、どのセグメントへのアドレスであるかを区別する方法もあります。PCから生成されたアドレスであればコードセグメント、スタックポインタから生成されたアドレスであればスタックセグメント、それ以外であればヒープセグメントと区別します。

上記の説明をコードで表現すると、以下のコードのようになります。もし、SEG_MASK、OFFSET_MASKが理解できない読者がいるなら、ネットワークのサブネットマスクと原理が同じなので、一度調べてみてください。

// get top 2 bits of 14-bit VA
Segment = (VirtualAddress & SEG_MASK) >> SEG_SHIFT
// now get offset
Offset = VirtualAddress & OFFSET_MASK
if (Offset >= Bounds[Segment])
    RaiseException(PROTECTION_FAULT)
else
    PhysAddr = Base[Segment] + Offset
    Register = AccessMemory(PhysAddr)

セグメントの共有

空間をさらに節約する方法として、無駄な空間を減らすこともありますが、他の空間を再利用することもメモリ空間を節約することが可能です。プロセス間で共通のセグメントを共有すれば、メモリ部分の再利用が可能になります。特に、コードセグメントのような場合、この方法で多く再利用されます。

このような方法を安全に実装するためには、各セクションのセキュリティ範囲を示す必要があり、ハードウェアにそれを実行するビットが追加で必要になります。

OSが解決しなければならない問題

コンテキストスイッチ

プロセスが切り替わるときには、各セグメントの情報を保存し、交換する必要があります。

セグメントのサイズ変更

あるプログラムがmalloc()を呼び出してオブジェクトを割り当てたとき、ヒープ空間が不足していてさらに空間が必要であれば、OSは空きスペースを見つけてヒープセグメントのサイズを増やす必要があります。(UNIXのsbrk()メソッドがこの役割を果たします。)

未使用の物理メモリの管理

最も重要な問題です。セグメントを割り当てて有効な範囲を見つけても、その範囲が離れていると、それはあまり意味がありません。各有効なセグメントが物理的に接していれば、よりメモリを効率的に使用できるでしょう。