xv6の構成要素の継続の分析

  • 清水 隆博
    • 琉球大学大学院理工学研究科情報工学専攻
  • 河野 真治
    • 琉球大学工学部

研究目的

  • アプリケーションの信頼性を向上させるたい
    • その為には土台となる OS 自体の信頼性を高く保証したい
  • OSそのものも巨大なプログラムである
    • テストコードを用いた方法で信頼性を確保する事が可能
  • しかし並列並行処理などに起因するバグや、そもそもOSを構成する処理が巨大
    • テストで完全にバグを発見するのは困難
    • テスト以外の方法でOSの信頼性を高めたい

OSの信頼性

  • OSそのもの動作も保証されるべき
  • アプリケーションが行いたい処理の他に、 メモリやCPUの資源管理などが存在する
    • アプリケーション側からするとOSの機能
  • 本来行いたい処理
    • ノーマルレベルと呼ぶ
  • 資源管理など
    • メタレベルと呼ぶ
    • この別け方はOSの実装でも存在する
  • ノーマル、メタレベルの計算の両方を保証しないといけない

テスト以外で信頼性を高める方法

  • モデル検査
    • 実際に想定されるパターンを全て動かして検証する
    • デッドロック発生の検知
      • JavaPathFinderなど
  • 定理証明支援系
    • 論理学的なモデルに変更して証明する
      • Agda
      • Coq
  • OSをこれらの方法で信頼性を高めたい

OSの信頼性を高めるためには

  • 既存のOSのソースコードをそのまま使うのは困難
  • モデル検査の場合
    • OS自体をモデル検査する機能をOSに組み込む必要がある
  • 定理証明系の場合
    • Agda/CoqでOSを再実装する必要がある
    • それらのコードはそのままコンパイルする事ができない
  • ノーマルレベル/メタレベルを切り分けるのも困難
  • 動きつつ証明可能なOSを目指したい
    • これらを同時に達成出来るプログラミング言語でOSを実装する必要がある

Continuation Based C

  • ノーマルレベル/メタレベルの実装に適している言語
    • 通称CbC
  • C言語の下位言語であり、 いくつかのCコンパイラ上で実装している
    • gcc
    • llvm/clang
  • C言語の構文は使用可能だが、 関数呼び出しの他に軽量継続を持つ
    • 関数呼び出し時のスタックの操作を行わずjmp命令で次の処理に移動する
    • schemaなどと違い環境を持たず継続するために軽量継続と呼ぶ

CbCとCodeGear

  • 軽量継続で表現する単位をCodeGearと呼ぶ
  • CodeGearはCの関数とアセンブラの中間の様に使える
  • CodeGearは返り値の型の代わりに__codeで宣言する
__code cbc_read(__code (*next)(int ret)){
    struct file *f;
    int n;
    char *p;

    if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) {
        goto next(-1);
    }
    goto cbc_fileread(f, p, n, next);
}
  • cbcで書き直したxv6のreadシステムコールの例

CbCの呼び出し

  • CbCはgoto文で呼び出す
  • cbcで書き直したシステムコールディスパッチの例
    • 受け取ったシステムコール番号に対応するCodeGearに継続する
void syscall(void)
{
    int num;
    int ret;

    if((num >= NELEM(syscalls)) && (num <= NELEM(cbccodes)) && cbccodes[num]) {
        proc->cbc_arg.cbc_console_arg.num = num;
        goto (cbccodes[num])(cbc_ret);
    }
  • 呼び出し元には返ってこない

CbCを用いたソフトウェアの実装

  • CodeGearはスタックを持てない為にデータの管理やCodeGear自体の管理をローカル変数で行えない
    • 実際に利用するデータはスタック以外の場所で確保する必要がある
    • 確保した場所からメタ計算でデータを取り出し、 CodeGearに渡したい
  • OS内部でノーマルレベル/メタレベルの処理の切り分けをしたい
    • メタな計算を行うCodeGearは直接呼び出したくない
  • ノーマル/メタのCodeGearの切り分けと、データの管理をする構造を作成した

CbCのcotnext

  • CbCのプログラムで利用するCodeGearとデータの組を管理する機能
    • データをDataGearという単位として扱う
    • 計算で使用する各DataGearを実際に保存している
  • ノーマルレベルのCodeGear間を遷移するようにプログラミングする
    • ノーマルレベルからはCodeGearを直接操作できない
      • メタ計算中でCodeGearの番号をcontextでディスパッチする
    • この間にMetaCodeGearが 実行される
  • 実際のデータの入手、保存はcontextを触ることが出来るMeta Code Gearが行う

通常のCbCプログラム

  • プログラマから見るとCodeGearからCodeGearへの継続のみに見える

実際のCbCプログラム

  • 実際は1度contextを参照するMetaCodeGearに継続する
    • 番号から次のCodeGearに対応するMetaCodeGearを取り出す

実際のCbCプログラム

  • MetaCodeGearでは次の計算に必要なDataGearを取り出す
    • 全てのDataGearが確保できたらCodeGearにgotoする

CbCのcotnext

  • CbCのcontextを使うことでメタ計算とノーマルレベルの計算を切り分ける事ができる
  • 計算に必要な全てのデータはcontext上に保存される
  • 使用するCodeGearの組や入出力の情報もcontextに保存される
  • これらを用いてOSを書き換えたい

CbCを用いたOSの再実装

  • CbCのCodeGearは状態遷移単位での記述に向いている
  • 状態遷移を基本としたモデルに変換し、HoareLogicなどの形式手法を用いて信頼性を高めたい
  • CbCは比較的文法が簡易
    • Agda/Coqなどの定理証明からCbCへのコード変換が可能であると考えている
  • CbCや定理証明系を用いてアプリケーションとOSを再実装したい
    • 最初の段階として既存のOSをCbCで再実装する

xv6

  • マサチューセッツ工科大学で開発されたv6OSをもとにしたOS
    • x86向けにANSI Cで実装されている
  • 比較的小さなUNIX
    • 基本的な機能は実装されている
    • システムコール、 ファイルシステム、 プロセス処理...
  • Raspberry Pi上で動作を目指したARM用のバージョンも存在する
    • やっぱりRaspberryPiで動かしたい
    • 今回は ARMのバージョンをCbCで再実装する

xv6のCbCでの書き換え

  • 既存のOSを段階的にCbCで書き換えていく
    • CbCでOSを実装する際のプロトタイプ実装としての段階
  • 今回はシステムコール部分の一部、 メモリ管理部分、 ファイルシステムなどを書き換えた
  • CbCのcontextをプロセス構造体に埋め込み、 goto文を利用する場合はcontextからデータを参照する

read system callの書き換え

  • xv6のシステムコールの一部を書き換えることを検討する
  • 最初にread systemcallの処理をCodeGearへの書き換えを行った
  • readシステムコールなのでreadする対象によって処理が分岐する
    • ファイル
    • inode
    • コンソール
  • 読む対象によってCodeGearを書き換えた
    • スケジューラーに接続する箇所や、 sleepする箇所もCodeGearとして書き換える

read system callの継続の一部

  • 実際に処理を切り分けているCodeGear
    • ファイルのtypeによって継続先を変更する
__code cbc_fileread (struct file *f, char *addr, int n, __code (*next)(int ret))
{
    if (f->readable == 0) {
        goto next(-1);
    }

    if (f->type == FD_PIPE) {
        goto cbc_piperead(f->pipe, addr, n, next);
    }

    if (f->type == FD_INODE) {
        ilock(f->ip);
        proc->cbc_arg.cbc_console_arg.f = f;
        goto cbc_readi(f->ip, addr, f->off, n, cbc_fileread1);
    }

    goto cbc_panic("fileread");
}

read システムコールの状態遷移図

  • システムコール中のCodeGearを状態遷移図にした
    • 自然言語で説明可能となる利点がある

Basic Block単位での書き換え

  • 仮想メモリ管理やファイルシステムなどの関数はxv6の場合Cのファイル単位でまとまっている
    • CodeGear用のAPIをいきなり設計するのではなく、 段階的に書き換える
  • 今回はこれらの関数の内容をCodeGearで書き直した
    • 各関数への呼び出し時にダミーの関数を呼び出すことでCとCbCの相互移動が可能

メモリ管理部分の書き換え

まとめ

  • xv6の処理の一部を継続を用いてcbcで書き換えた
    • システムコールに着目する手法
    • 書き換える関数のBasic Blockに着目する手法
  • 部分的にCbCでxv6が書き換え可能なことが解った
  • 今後はxv6の完全な書き換えを目指す
    • ユーザーコマンド側の書き換えも検討する
  • xv6の証明可能な機能/構文の導入を目指す