Think Essentially

計算機テクノロジー全般が好きです

マイクロカーネル HinaOS に新規サーバを実装する

はじめに

“自作OSで学ぶマイクロカーネルの設計と実装”におけるレポジトリ内のIDEAS.mdに非常に刺激的な課題が記載されていたので、チャレンジしようと思います。ちなみに、当書籍は懇切丁寧にソースコードの解説が記載されており、OS実装の学習に最適であると思います。

また、私は課題に対して頭を使って試行錯誤する過程が一番力を付くと考えているので、大まかに全体に目を通して読み、まだ完璧には内容を理解していない状態で、課題に取り組んでいます。

実装概要

初歩的な取り組みとして、int型のデータを送ると、インクリメントして返すincサーバを立てます。

実装フロー

IDEAS.md 参照より、具体的な実装フローは以下のようになります。

1. `messages.idl`に新しいメッセージを定義し、一回 `make` を実行してメッセージの型定義を自動生成する。
2. `servers`ディレクトリに新しいサーバのディレクトリを作成する (`servers/pong`をコピーアンドペーストするとよい)。
3. サーバの実装を書く。
3. Makefileの`BOOT_SERVERS`変数に新しいサーバを追加する。すると、起動時に自動的にサーバが起動するようになる。
4. クライアント側の実装を書く。たとえばコマンドラインシェルにサーバと通信する新しいコマンドを追加する。

この手順に従い、サーバを実装していきます。

1. messages.idlに新しいメッセージを定義する

リモートプロシージャコール (rpc) 用のメッセージを定義します。文法自体はRust言語の関数定義のようです。

rpc inc(sock: int) -> (sock: int);

上記のような関数を定義し、makeを実行します。すると、libs/common/ipcstub.h において、

  • サーバへのメッセージ
  • サーバの返答用メッセージ

の2つに対応する構造体と定数が自動生成されました。

// libs/common/ipcstub.h 

struct inc_fields {
    int value;
};
struct inc_reply_fields {
    int value;
};

...

#define INC_MSG 63
#define INC_REPLY_MSG 64

2. serversディレクトリに新しいサーバのディレクトリを作成する

ヒントに従いpongディレクトリをコピペし、名前をincに。build.mkの効用はまだ分からないですが、まとめてコピーします。

3. サーバの実装を書く。

pingの実装を踏襲します。内容は非常にシンプルなもので、受け取った値をインクリメントして返すというものです。

// servers/inc/main.c

void main(void) {
    ASSERT_OK(ipc_register("inc"));
    TRACE("ready");

    while (true) {
        struct message m;
        ASSERT_OK(ipc_recv(IPC_ANY, &m));
        switch (m.type) {
            case INC_MSG: {
                DBG("received ping message from #%d (value=%d)", m.src,
                    m.inc);

                m.type = INC_REPLY_MSG;
                m.inc_reply.value = m.inc.value++;
                ipc_reply(m.src, &m);
                break;
            }
            default:
                WARN("unhandled message: %s (%x)", msgtype2str(m.type), m.type);
                break;
        }
    }
}

主にIPCで扱うメッセージの構成は以下のようになります。union型内で示されているIPCSTUB_MESSAGE_FIELDSは、メッセージの種類、typeに対応する構造体になります。

struct message {
    int32_t type;  // メッセージの種類 (負の数の場合はエラー値)
    task_t src;    // メッセージの送信元
    union {
        uint8_t data[0];  // メッセージデータの先頭を指す
        /// 自動生成される各メッセージのフィールド定義:
        //
        //     struct { int x; int y; } add;
        //     struct { int answer; } add_reply;
        //     ...
        //
        IPCSTUB_MESSAGE_FIELDS
    };
};

4. シェルコマンドの実装

シェルから、incサーバにアクセスするコマンドを実装します。これもまた非常にシンプルで、inc <value> といった文法です。

// servers/shell/main.c

static void do_inc(struct args *args) {
    if (args->argc != 2) {
        WARN("Usage: inc <VALUE>");
        return;
    }

    task_t inc_server = ipc_lookup("inc");
    int value = atoi(args->argv[1]);

    // incサーバにメッセージを送信する
    struct message m;
    m.type = INC_MSG;
    m.inc.value = value;
    ASSERT_OK(ipc_call(inc_server, &m));

    // incサーバからの応答が想定されたものか確認する
    printf("reply: %d\n", m.inc_reply.value);
    ASSERT(m.type == INC_REPLY_MSG);
}

実装した関数をもとに、コマンド設定に追記します。.nameがシェルに打つコマンド名、.run_incがそのバイナリに対応する処理、.helpがhelpコマンドを打ち込んだ際に表示される説明文ですね。

// servers/shell/main.c

static struct command commands[] = {
        ...
    {.name = "inc", .run = do_inc, .help = "incremnt given number"},
    ...
};

自動サーバに追記を行います。

# Makefile

# 自動起動するサーバのリスト
BOOT_SERVERS ?= fs tcpip shell virtio_blk virtio_net pong inc

動作確認

いざ、実行! 正常に機能しました!これでHina OS のハックに成功しました!笑

まとめ

Hina OSは大変面白い題材です。是非次の課題にも取り組んでいこうと思います。

参考文献

https://www.amazon.co.jp/自作OSで学ぶマイクロカーネルの設計と実装-怒田晟也-ebook/dp/B0C52SFYDC/ref=sr_1_1?__mk_ja_JP=カタカナ&crid=17NO089IHFDL0&keywords=マイクロカーネル&qid=1687662077&s=books&sprefix=マイクロカーネル%2Cstripbooks%2C166&sr=1-1

test

test

test