おがさわらなるひこのオープンソースとかプログラミングとか印刷技術とか

おがさわらなるひこ @naru0ga が技術系で興味を持ったりなんだりしたことをたまーに書くブログです。最近はてなダイアリー放置しすぎて記事書くたびにはてな記法忘れるのではてなブログに移行しました。

クリエイティブ・コモンズ・ライセンス
特に断りがない場合は、本ブログの筆者によるコンテンツは クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンスの下に提供されています。

コンピュータの知識を得ようぜ 第002-003回

探したんだけど第002回のエントリがないや。書き忘れかな。
ということでまとめて。

第001回で、MITのOpenCourceWare6.828 Operating System Engineeringテキストをダラダラ読むという形式でスタート。

Lecture 1. OS Overviews

OS概論というふれこみなんだけど、なぜかシステムコールを延々列挙してみたり、唐突にシェルスクリプトの実装をはじめてみたりという謎な構成。
でも案外標準入出力とかリダイレクションとかパイプの実装ってどうなってるのって話を思い出すのは新鮮だった。

Lecture 2. x86 and PC architecture

PC のアーキテクチャの話はおおざっぱに飛ばして 86 のアーキテクチャの話をしていたような気がする。

  • x86レジスタの直交性が低い(たとえばDIVとかはAXレジスタにしかできない)のがめんどくさいよねとか
  • もう死語になっちゃったセグメントの話とか far / huge といったポインタの種類とか
  • メモリマップトI/OとI/OマップトI/Oの話とか
  • メモリ空間と物理メモリは必ずしも同じじゃないよって話とか
  • gcc の calling convention の話から、C call と Pascal call の話とか
    • 一見素直な Pascal に比べると C の calling convention は変態なんだけどなんでだかわかりますよね? とかいう話とか*1
    • WindowsAPI コールであるところの WINAPI とか stdcall というのは実は Pascal call のことなんだけど、これはアセンブラで書くときにこっちの方がわかりやすいってことと、あと Classic MacPascal Call だったからという話
    • まあアセンブラを書けなくてもいいけど、読めるのは今でも大事なスキルだよねとか
  • それから唐突にCPUエミュレータを書く話で終わり。

ここまで第002回。ぜんぜんOSの話になりませんな、といいつつおしまい。


さて第003回は4/9に行われました。

Lecture 3: Operating system organizaton (途中まで)

やっとOS論らしい話になってきました。

Virtualization (仮想化)
  • 第001回の私のしょぼしょぼ資料でも書いたけど、OSの重要な役割は物理的に一個しかない物を複数に見せること (multiplexing)
  • 言い換えればコンピュータの仮想化
    • あるアプリが保存したデータが他のアプリのデータを破壊しちゃダメだし
    • jmp 命令で他のアプリに飛んでけちゃうとマズいし
    • 一個の仮想マシンがプロセッサを握ってしまっては困る*2

なぜだったか忘れたけどここでファイルシステムの話になって、ZFS のセクタ割り当て戦略はメモリのアロケーション戦略とすごく似てるんだよ、みたいな話をしたような気がする。

プロセッサの仮想化
  • 一番簡単なやり方は「一度に一個のプロセス」にする
    • OS 登場以前のやり方
    • 状態を取っておいて実行して戻して、を繰り返すだけ
    • 超簡単
  • 取っておくべき情報は?
    • プロセッサによるけど x86 なら ESP, EIP, あと他のレジスタ
  • これじゃやだ!というなら(つまり時分割処理)、
    • 割り込みによってプロセッサを切り替えるような処理が必要
    • 同じメモリにみんなでアクセスするのはイケてないのでメモリを分割することも必要
メモリ分割

方法は二つ

  1. 高水準で型安全な言語でプログラムを書くように強制する
  2. ハードウェアでサポートする

もちろん両方を組み合わせることもできるけど、実際はハードウェアサポートだよねということでこれを考えましょう。


ここで簡単のために「物理メモリは無限にある」と仮定しましょう。

  • メモリ管理ユニット (Memory Management Unit; MMU) というデバイスをプロセッサとメモリの間に突っ込む*3
  • MMU はメモリアクセスをドメインレジスタというものを使って監視する
  • ドメインレジスタはそのときにプロセッサがアクセスできるアドレスの範囲を決める
  • アプリケーションがスイッチしたら、ドメインレジスタが切り替わる

これでメモリ分割が可能になりました。


もし一部のメモリをアプリケーション間で共有したいとか、そこに格納されたプログラムをみんなで実行したいとかある場合、読み込み(R)/書き込み(W)/実行(X)といったフラグをドメインレジスタに持たせればいい。

んで、ドメインレジスタが破壊されないようにするにはどうすればいいかというと、「カーネルモード」という概念をプロセッサに導入して、「カーネルモード」でしかドメインレジスタはさわれないようにする。この概念については後述。

メモリ管理

ここでも物理メモリは無限にあって、あとアドレス空間は十分にあるという仮定をおきます。
んで、ドメイン(=アプリケーションごとに割り当てられるメモリ)を作ったり、拡張したり、逆に縮小したりといった機能を考えてみましょうと。ただし、ここでドメインは連続性を持つことを保証したいとします。

となると、ドメインを作成するときに、単純に続いてポンポン作っていくのはとてもイケてないことがすぐわかります。というのはあるドメインを拡張しようとしたら、もう上も下も他のドメインに押さえられているので、連続性を保証したいと思ったらガバッとコピーするしかない。

ということで、物理アドレスアドレス空間を仮想化しようというアイディアが生まれます。
アドレス空間というのはアプリから見るとただのメモリに見えるんですが、物理的にどう割り当てられるかはOSやハードウェア次第ですよと、そーゆーアイディアです。
それぞれのアプリにアドレス空間を割り当て、そこのアドレス(仮想アドレス)をそれを物理アドレスに変換する方法はもちろんMMUを用いて変換テーブルを使ってマッピングするわけですが、その方法は三つ。

  1. 全部の仮想アドレスと物理アドレスの表を作る。もちろん、こんなばかげたまねはしません。アドレスの分だけ巨大な表だなんて!
  2. x86 のセグメントの考えを応用し、連続的な仮想アドレスを (セグメント#, 開始(物理)アドレス, 長さ) に変換する表を持つ
  3. 物理メモリを固定長の「ページ」に分割し、ページマップ (ページ# をブロック# に変換する) を使う。ページマップとしては配列、木構造、スーパーページ、などがある

ちょっと最後のがわかりにくいけど、まあ追々説明があるのかもしれません。
あるいはビデオ見るかするとちゃんと説明されてるのかなあ。

なお CPU アーキテクチャによってこれらのうち複数をサポートしている物があり、たとえば x86 は 2 と 3 の両方をサポートしているそうです。


あとここで唐突にOSブートシーケンスの話がちょっとだけ出てきます。

ちなみに MMU 有効化前にうっかり MMU が必要なメモリ割り当てとかやるといろいろ悲しいので注意。おぼろげな記憶では WindowsWDM だと同じエントリポイントでページング有効なモードと無効なモードとで呼ばれることがあって、たいていは前者なんだけどまれに後者で呼び出されて、メモリ割り当てしようとして青画面、というのはよくあるハマリパターンだった気がする。

オペレーティングシステムの構成

テキスト丸写しは飽きてきた。
まずは言葉の定義で、カーネルとかライブラリとかアプリケーションとかオペレーティングシステムといった言葉をあらためて定義しなおしてます。
シェルとかどうなの? というところに疑問はありますが、そこはひとまず起きます。


んで、OSの設計にはざっくりと次の四つがありますよと。この講座のレクチャーノート、唐突に話が始まったりするわりにはこういう説明は丁寧なんだよな。まあ、割とありがちなんで引き写しはやめて

最後のが、???という感じなんですが、次からちゃんと説明するよ、ということで、この回は終了。

今のところの感想

  • レベル的にもボリューム的にもあと英語的にも、OSをバリバリハックしたことがない(大学でOS概論なんて授業聞いたような気もするなあ、というレベル)私みたいな人にはちょうどいい気がする。
  • 講義のビデオを見ている訳じゃないのだけど Lecture Note だけ見てると「あれ?なんでここでその話題が?」ということが多々ありますね。
  • 題材として取り上げている Xv6(Wikipedia) OS は wc -l *.c で 6712 行と非常にコンパクトなのでいい感じ。

つことで次回も楽しみですね。
早くコードの解説にたどり着くといいな。

*1:もちろん、可変長引数があるからです。

*2:ここで Win16 の non-preemptive multitasking の話をしたような気がする。

*3:今のプロセッサは MMU の機能を持っているのが当然ですけど。