Advent Calendar 2018: advos
Day 1: 環境の構築
まず最初に開発環境を構築しようと思います。私は普段はmacOS上で開発するのですが,残念なことにmacOSの標準リンカ(/usr/bin/ld
)の-T
オプションが使えません。そのため,OSをBIOSから実行するバイナリを生成できません。いままでは,FreeBSDのクロスコンパイラ(gcc)をインストールしていましたが,開発環境の統一化とテスト自動化・CIを考えて今回はDockerを使った開発環境の構築をしてみようと思います。
と言ってもDockerはLinuxコンテナなので,自作OSを直接動かすことはできません。ですので,仮想マシン上で実行します。仮想マシンの実行環境としてQEMUを使います。DockerでGUI (X)を使うには,クライアントを入れたり設定したりしないといけないので,今回はすべてCLIでやります。CLIだけで完結させるためにうQEMUをcursesモードで使います。ただし,cursesを使う都合上,CTRL-Cなどのプロセスへのキーボード入力が効かないので,強制終了させたいときにはESC-2
(またはALT-2
)でQEMUのモニタコンソールを開き,quitと打ち込むことで終了できます。
DockerとQEMUを使って開発環境と実行環境を整えますが,今回はそれに加えてdocker-compose
を使おうと思います。個人的にはYAMLが好きではないのですが,PXEブートに対応するときにDHCPサーバとTFTPサーバの連携が楽だと思われるためです。今回PXEブートまで対応できるかはわかりません。ただし,docker-compose.yml
のサービス定義内にtty: true
を指定すればttyが使えるはずなのですが,docker-compose
の標準出力がサービスのttyと衝突するためか,hangしてしまうので,cursesベースのUIを使うときはdocker-compose up
ではなくdocker-compose run
で実行します。PXEブートするときはどうするか決めていないです。
macOSへのDocker環境のインストール
macOSでDocker環境を構築するには仮想マシン上にDocker用のLinuxをインストールする必要があります。そのため,VMを管理するdocker-machine
というパッケージが必要になります。docker
およびdocker-compose
にあわせて,docker-machine
も以下のようにbrew
でインストールします。
$ brew install docker docker-machine docker-compose
macOSではVM上のLinuxを使うため,ハイパーバイザが必要です。今回はVirtualBoxを使います。DockerのホストLinuxマシンをVirtualBox上にインストールするには以下のコマンドを使います。
$ docker-machine create --driver virtualbox dockerdev
このDockerのホストLinuxを起動するには次のコマンドを使います。
$ docker-machine start dockerdev
少しややこしいですが,今回開発するOSは,「macOS」上の「DockerのホストLinux」上の「Dockerコンテナ」上の「QEMU」上で動きます。VirtualBox上でも直接動かすこともできますが,コンパイル環境の構築が面倒なので,今回はこうします。
インストールしたDockerのホストLinuxをmacOSのdocker
コマンドやdocker-compose
コマンドから使うために以下のコマンドで環境変数を設定します。
$ eval $(docker-machine env dockerdev)
これでmacOSへのDocker環境の構築は終了です。eval $(docker-machine env dockerdev)
はシェルの起動スクリプトに入れておくと良いと思います。
レポジトリのディレクトリ構成
ディレクトリ構成は以下のようにします。OSのソースコードはsrc
に入れます。
src
: OSのソースコードboot
: ブートローダ
qemu
: OSを実行するQEMU用Dockerfile
置き場
今日は環境構築が目標なのでブートローダについては明日以降にまわそうと思います。ただ,何も実行できないと悲しいので,src/boot/mbr.SのようにBIOSの機能を使ってメッセージを表示するだけのアセンブリプログラムとOSイメージを作成するためのMakefile
を置いています。これらのファイルについては明日説明するつもりです。
QEMUでOSを実行するコンテナのDockerfile
自作OSのコンパイルおよび実行を行うコンテナを定義します。以下にDockerfile
を書きます。
FROM ubuntu:16.04
MAINTAINER Hirochika Asai <panda@jar.jp>
## Install build-essentials and qemu
RUN apt-get update
RUN apt-get install -y --no-install-recommends build-essential qemu-system
## Copy source to the workdir
COPY src /usr/src
WORKDIR /usr/src
RUN make
## Run the OS with qemu
CMD ["qemu-system-x86_64", "-m", "1024", \
"-drive", "id=disk,file=advos.img,if=none", \
"-device", "ahci,id=ahci", \
"-device", "ide-drive,drive=disk,bus=ahci.0", \
"-boot", "a", "-display", "curses"]
ベースのイメージとしてUbuntu 16.04を使います(1行目)。そこにapt
でコンパイル環境としてbuild-essential
,実行環境としてqemu-system
をインストールします(7行目)。10行目のCOPY src /usr/src
でレポジトリ内のsrc
ディレクトリの中身を全てコンテナ内の/usr/src
にコピーし,make
でMakefile
の定義通りにコンパイルします。後述しますが,このmake
でOSの実行イメージがadvos.img
として作成されます。最後にCMD
でQEMUでそのOSイメージを実行します。
docker-compose.yml
上述のDockerfile
でOSのソースファイルをコピー,コンパイルし,QEMUで実行するコンテナを定義しました。OS開発は原則単一のサービス(QEMUのみ)でできますが,例えばPXEブートのように他のサービスと組み合わせるときに構成管理をしておくと便利なので,今回は勉強も兼ねてdocker-compose
を使います。
docker-compose.yml
は以下のように最低限のものにしています。OSをQEMU以外の実行環境(例えばbochs)でテストするかもしれないので,Dockerfile
をsrc
ディレクトリやルートに置かず,context
をレポジトリのルートにしてqemu
ディレクトリ下のDockerfile
を指定するようにしました。
version: '3'
services:
qemu:
build:
context: ./
dockerfile: ./qemu/Dockerfile
Dockerコンテナの実行
これでOS開発環境は整いましたので,試しにコンパイルおよび実行をしてみます。
$ docker-compose build
でqemu/Dockerfile
に従い,ビルドツールやQEMUをインストールし,advosイメージをコンパイルしたコンテナを構築します。
$ docker-compose run qemu
を実行すると,curses画面でCLIに
Welcome to advos
と表示されると思います。いまの状態のadvosにはシャットダウン機能が実装されていないので,ESC-2
またはALT-2
で以下のようなQEMUのコンソール画面に遷移できるので,こちらからquit
と打って終了してください。
compat_monitor0 console
QEMU 2.5.0 monitor - type 'help' for more information
(qemu)
まとめと明日の予定
今日はDockerとdocker-composeを使ったOSの開発環境・実行環境を整えました。明日はBIOSから自作OSを起動するためのブートローダについて書こうと思います。