自作OS×自作ブラウザで学ぶ
Webページが表示されるまで
サポートページ

About

このページは、Web+DB Press Vol.120に掲載されている記事「自作OS×自作ブラウザで学ぶ Webページが表示されるまで」に関する補足情報をまとめたページです。

本ページの内容に関するお問い合わせは、著者である@hikaliumおよび@d0iasmまで、Issues経由でお願いします。

プロンプトの表記について

誌面および本ページ内では、各コマンドの前に、どの環境でそのコマンドを実行するべきなのかわかるよう、プロンプトをつけている場合があります。

$ something                 # 通常のLinuxホスト上で実行するコマンド
(host)$ something           # ホスト上で実行することを明確にしたい場合
(liumos)$ something         # liumOS上で実行するコマンド
(liumos-builder)$ something # Dockerインスタンス上のLinuxで実行するコマンド

このシェル($以前)の部分は実際には入力する必要はありません。

各章ごとの補足

事前準備

liumOSのレポジトリはgithub:hikalium/liumosから取得することができます。

(host) git clone https://github.com/hikalium/liumos.git

以下の各コマンドについては、wdb_120ブランチで動作することを確認しています。

環境構築の手間を省くため、Dockerを利用する方法を誌面では説明しています。Dockerのインストール手順については公式ページの情報をご参照ください。

Docker環境の外でアプリケーションやOSをビルドする場合には、README.mdに記載の環境構築を事前に行ってください。

第1章

Dockerのインストールについては事前準備を参照してください。

Dockerなしで実験したい場合には、README.mdのSetup tap interface (for linux)の項に記載の手順で設定を行ってください。ただし、この方法はコンピューターのネットワーク設定を変更するため、特にリモートマシンで試される際には細心の注意を払って行ってください。

第2章

この章で実装するping.binのソースコードはapp/ping/ping.cから確認できます。

liumOS上で動作確認をしたい場合は

(host) make run_docker

を実行してliumOSを起動します。初回起動時は、Dockerコンテナのイメージをダウンロードするため、多少時間がかかりますが、次回以降は数秒で完了します。

次に、起動したliumOSのシリアルコンソールに、以下のコマンドでアタッチします。

(host) telnet localhost 1235

接続した直後はプロンプトが見えませんが、Enterキーを押すと以下のようにプロンプトが出てくるはずです。(出ない場合は自作OSの起動に失敗している可能性がありますので、再度make run_dockerを実行してください。)

$ telnet localhost 1235
Trying ::1...
Connected to localhost.
Escape character is '^]'.
# ここでEnterキーを押す

Not a command or file: 
(liumos)$ # プロンプトが出てきた

その後、pingコマンドを実行してみます。

(liumos)$ ping.bin 10.0.2.2
ping.bin 10.0.2.2
Ping to 10.0.2.2...
kernel: sys_socket: socket (fd=3) created (IPv4, DGRAM, ICMP)
kernel: dst is in the same subnet.
kernel: ARP request sent to 10.0.2.2...
kernel: ARP entry found!
recvfrom returned: 8
00 00 FF FF 00 00 00 00 
ICMP packet recieved from 10.0.2.2 ICMP Type = 0

うまくいかない場合は、一度すべてのシェルを閉じて、最初のmake run_dockerのステップからやり直してみてください。

第3章

この章で言及するソースコードの全体像は、下記から確認できます。

(liumos) udpclient.bin -> (Linux) udpserver.bin

(host)$ make run_docker
(host)$ docker exec -it liumos-builder0 /bin/bash
(liumos-builder)$  app/udpserver/udpserver.bin 8888
(host)$ telnet localhost 1235
(liumos)$ udpclient.bin 10.0.2.2 8888 Hello!

(liumos)$ udpserver.bin <- (Linux) udpclient.bin

(host)$ make run_docker
(host)$ docker exec -it liumos-builder0 /bin/bash
(host)$ telnet localhost 1235
(serial)$ udpserver.bin 8889
(liumos-builder)$ app/udpclient/udpclient.bin 127.0.0.1 8889 hello

第4章

第4章で使用するソースコードは以下の通りです。

liumOS上のhttpclient.binと、Linux上のhttpserver.binの通信

(host)$ make run_docker

// server
(host)$ docker exec -it liumos-builder0 /bin/bash
(liumos-builder)$ /liumos/app/httpserver/httpserver.bin --port 8888

// client
(host)$ telnet localhost 1235
(liumos)$ httpclient.bin --ip 10.0.2.2 --port 8888 --path /index.html

Linux上のhttpclient.binと、liumOS上のhttpserver.binの通信

// server
(host)$ telnet localhost 1235
(liumos)$ httpserver.bin --port 8888

// client
(host)$ docker exec -it liumos-builder0 /bin/bash
(liumos-builder)$ /liumos/app/httpclient/httpclient.bin --ip 127.0.0.1 --port 8888 --path /index.html

ローカルのLinux環境で試す場合

// server
$ ./httpserver.bin --port 8888

// client
$ ./httpclient.bin --ip 127.0.0.1 --port 8888 --path /index.html

もし、ポート番号が他のアプリケーションですでに使用されていると、Error: Failed to bind a socketというメッセージが出ます。その場合は--portで指定するポート番号を変えてください。

訂正

第4章でHTTPサーバを実装するさいのリクエストを待ち受けるコードに間違いがありました。address変数をbind()する必要があります。

間違い(誌面)

address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);

recvfrom(socket_fd, request, SIZE_REQUEST, 0,
         (struct sockaddr*) &address, &addrlen);

訂正

address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);

// 訂正箇所: bind関数によって、address変数の情報とsocketを結びつける必要があります。
if (bind(socket_fd, (struct sockaddr *) &address, addrlen) < 0) {
  Println("Error: Failed to bind a socket");
  exit(EXIT_FAILURE);
}

// 補足: recvfrom関数によってaddress変数はクライアントの情報へと上書きされます。後にsendto関数によってクライアントへレスポンスを送る際にaddress変数を再度使用します。
recvfrom(socket_fd, request, SIZE_REQUEST, 0,
         (struct sockaddr*) &address, &addrlen);

第5章

第5章で使用するソースコードは以下の通りです。

liumOS上のブラウザとLinux上のサーバで試す場合

// run docker
(host)$ make run_docker

// server
(host)$ docker exec -it liumos-builder0 /bin/bash
(liumos-builder)$ /liumos/app/httpserver/httpserver.bin --port 8888

// client
(host)$ telnet localhost 1235
(liumos)$ browser.bin --url http://10.0.2.2:8888/index.html

その他のURLを使用したbrowser.binのコマンド

  • browser.bin --url http://10.0.2.2:8888/page1.html
  • browser.bin --url http://10.0.2.2:8888/page2.html
  • browser.bin --url http://10.0.2.2:8888/not_exist.html

ローカルのLinux環境で試す場合

// server
$ ./httpserver.bin --port 8888

// client
$ ./browser.bin --url http://127.0.0.1:8888/index.html

もし、ポート番号が他のアプリケーションですでに使用されていると、Error: Failed to bind a socketというメッセージが出ます。その場合は--portで指定するポート番号を変えてください。

既知の問題

Docker上でping.binが動作しない

$ docker exec -it liumos-builder0 /bin/bash
(liumos-builder)$ app/ping/ping.bin 8.8.8.8
Ping to 8.8.8.8...
socket() failed

おそらく筆者が用意したDocker環境の問題です。解決策が見つかり次第修正します。

それまでは、Linux環境でDockerを使わずにliumOSの環境を整備して直接ビルドして実験するか、Linux上で動くかどうかのテストはスキップして、自作OS上でping.binを実行してみてください。(自作OS上で動かす方が簡単だなんて不思議ですね…。)

Virtio::Net not initialized yetというエラーでOSが起動しないんだけど…

自作OS側のバグで、現在修正中です。

liumos/issues/54

再起動すれば多くの場合でうまくいくはずです。お手数をおかけしますが、もう一度お試しください。(もしくは、自作OSのデバッグの大変さを味わうというのもまた一興でしょう。)

Author

License