クラウド向けOS「OSv」とは何か - ソースコードから調べてみる -
なにこれ
KVM開発エンジニアらが立ち上げたベンチャーCloudius、クラウド向けOS「OSv」を発表 | OSDN Magazineで紹介されていたので興味を持って弄ってみた。
KVM/Xen専用のOSで、アプリケーションとしてJavaアプリケーションが動くという事のようだ。
基本的にはJava製のWebアプリを想定しているのだろう。
ライセンスはBSDライセンス。
Ubuntuでのビルド方法
sudo apt-get install openjdk-7-jdk sudo apt-get install autotools-dev libltdl-dev libtool autoconf autopoint sudo apt-get install libboost-all-dev genromfs zfs-fuse autoconf ant git clone https://github.com/cloudius-systems/osv cd osv git submodule update --init make external all
実行方法
sudo ./scripts/run.py -n -v
と実行するとQEMUが起動してこんな感じのコンソールが立ち上がる:
SCRIPT, tap0 acpi 0 apic 0 acpi 1 apic 1 acpi 2 apic 2 acpi 3 apic 3 APIC base fee00000 Loader Copyright 2013 Cloudius Systems locale works VFS: mounting ramfs at / VFS: mounting devfs at /dev ACPI: RSDP 0xfd8d0 00014 (v00 BOCHS )ACPI: RSDT 0x3fffe380 00034 (v01 BOCHS BXP CRSDT 00000001 BXPC 00000001)ACPI: FACP 0x3fffff80 00074 (v01 BOCHS BXPCFACP 00 000001 BXPC 00000001)ACPI: DSDT 0x3fffe3c0 011A9 (v01 BXPC BXDSDT 00000001 I NTL 20100528)ACPI: FACS 0x3fffff40 00040ACPI: SSDT 0x3ffff6e0 00858 (v01 BOCHS BXPCSSDT 00000001 BXPC 00000001)ACPI: APIC 0x3ffff5b0 00090 (v01 BOCHS BXPCAPIC 00000001 BXPC 00000001)ACPI: HPET 0x3ffff570 00038 (v01 BOCHS BXPCHPET 0000000 1 BXPC 00000001)RAM disk at 0x3eb59000 (4096K bytes) Initializing network stack... Done! eth0: ethernet address: 52:54:0:12:34:56 VFS: mounting zfs at /usr zfs: mounting osv/usr from device /dev/vblk0.1 [I/34 dhcp]: Waiting for IP... [I/325 dhcp]: Configuring eth0: ip 192.168.122.89 subnet mask 255.255.255.0 gateway 192.168.122.1 [I/325 dhcp]: Server acknowledged IP for interface eth0 stub dlclose() OpenJDK 64-Bit Server VM warning: Can't detect initial thread stack location - find_vma failed device random not found SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. Warning: default mime table not found: /usr/lib/jvm/jre/lib/content-types.properties vfork stubbed ____ _____ / __ \ / ____| | | | | (_____ __ | | | |\___ \ \ / / | |__| |____) \ V / \____/|_____/ \_/ [/]% Puma 2.5.1 starting... * Min threads: 0, max threads: 16 * Environment: development * Listening on tcp://0.0.0.0:8080 == Sinatra/1.4.3 has taken the stage on 8080 for development with backup from Puma [/]% ls dev/ tmp/ tests/ etc/ tools/ testrunner.so java/ java.so usr/ [/]% help Try one of these commands with the -h or --help switch: NAME DESCRIPTION cat concatenate files and print on standard output cd change the current working directory dashboard dhclient env display the term env filter A filter for a stream of map help provides basic help java Java application launcher jdbc JDBC connection jmx Java Management Extensions jndi Java Naming and Directory Interface jpa Java persistance API jvm JVM informations log logging commands ls list directory contents man format and display the on-line manual pages md5sum perf pwd print name of current working directory shell shell related command sleep sleep for some time sort Sort a map system vm system properties commands test thread JVM thread commands
内部構造概要
ここに色々と書いてあるが、要するにVMM上でOpenJDKをスタンドアロン起動出来るようにしたという話に読める。
メモリ空間の切り替えによる保護もカーネル・ユーザモード切り替えによる保護もなく、ユーザプログラムからカーネルのAPIは関数コールで叩ける、システムの保護はJVMがやればいいというような事が書いてある。
JVMとJVMを走らせるのに最低限必要なC/C++のコードが走っているだけなんじゃないだろうか。
ソースコード調査(ドライバ編)
drivers以下にドライバのコードが置いてあった。
C++でクラスも継承も使ってオブジェクティブに書かれたコードだが、定数の名前とかからなんとなくBSDのドライバのにおいがするような気がする…
virtio-netのドライバでは
#include <bsd/sys/net/ethernet.h>
とか
struct mbuf *m
とか出てきて、ネットワークスタックはBSDのものを使っているのねという察しが付く。
ドライバの数から分かるように、実機起動は考えられていない。virtioが前提。
ソースコード調査(BSD編)
ネットワークスタックやZFSなどの機能を提供するためにFreeBSDのソースコードを流用しているようだ。
bsd/sys以下にFreeBSDカーネルのコードが置いてある。
不要な部分は容赦なく#if 0されており、必要なルーチンだけC++のAPIから呼び出す形で実装されている。
ZFSはbsd/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/のあたり、ネットワークスタックはbsd/sys/net・bsd/sys/netinetのあたり。
ネットワークスタックの初期化はbsd/net.ccで行っている。
OSv内のAPI↔FreeBSDカーネルのAPI双方向のグルーコードはbsd/portingに集められている。
例えば、bsd/porting/networking.cではNICをifupするAPIが実装されている。
ネットワークスタックはbsdディレクトリ以下で完結しているようなのだが、ファイルシステムはfs以下にVFSと幾つかのFSが登録されていて、ZFSはその一つとして扱われている(他の二つはramfs、devfs)。
具体的には、bsd/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.cに定義されているzfs_vfsopsがfs/vfs/vfs_conf.cで参照されている。
ソースコード調査(ブート周り)
詳しく追ってないが概要だけ。
ブートの初期段階はarch/x64のboot*.S、entry*.Sの辺りから起動してきて、C++のコードを実行可能な初期化を行う。
アセンブリコードが終わるとloader.ccのpremain()、main()が実行される。
ここから先述のドライバ、ネットワークスタック、ファイルシステム等々が一通り初期化される。
この先の処理がどう書かれているのか読み切れていないが、最終的にelf::programで/java.soにある引数を渡し(引き数値がどこから来るのかよくわかってない)、スレッドを立ててスレッド内でロード&実行するようだ。
ソースコード調査(java.so)
elf::programの正体はcore/elf.ccで、C++で書かれたELFローダになっている。
ELFローダはあるんだが、ユーザが操作出来るシェルは恐らくELFバイナリを実行出来るようにはなっていなくて、JVMを呼び出す機能しかない気がする。
ともかく、こいつで/java.soをロードして、loader.ccから/java.soのmain関数を実行する(.soとなっているのにmainがあるのは何か変な感じがするが…?)。
他にもcore/ディレクトリにはカーネルの核となる機能が幾つか突っ込まれている模様。
/java.soの正体はjava/java.ccで、これは先ず最初に/usr/lib/jvm/jre/lib/amd64/server/libjvm.soをロードしている。
これは、OSvのビルドステージでバイナリ形式で持ってきたLinux/amd64用のOpenJDKそのものである。
ダイナミックリンクライブラリとしてロード後、JVMが提供するC API経由でVMを起動、ライブラリをロードしている。
何故JVMをOSvにあわせて改変し、直接リンクしてしまわずにこんな回りくどいことをしているのだろうと思ったのだが、恐らくGPL汚染対策ではないだろうか。
OpenJDKのLinux版DLLを無改変で実行する為に、これに合わせてローダやAPIやシステムコールハンドラなどをOSv側に用意しているようだ。
ソースコード調査(libc)
OpenJDKを動かすのに使うのか、libcが用意されている。
このlibcはFreeBSDのじゃなくてmusl libcらしい。
何故これを使うのかは良く分からないが。
ソースコード調査(Linuxシステムコール)
OpenJDKがlibcを通さずに直接呼ぶのか、linux.ccには__NR_gettidと__NR_clock_gettimeをハンドルするシステムコールハンドラが用意されている。
ソースコード調査(CLI)
CLIはCRaSH the shell for the Java Platformが使われているらしい。
コマンドはmgmt/crash/src/main/resources/crash/commands/cloudius以下にある.groovyファイルで定義されているようだ。
名前の通り、Groovyで書かれている。
感想
JVMをスタンドアロンで動かす仕組みと言っているように見えるにも関わらず、ソースコードをいくら追ってもJVMが出てこないので最初とても困惑した。
実際に動かしてみたが、遊んでいるうちに「malloc_large(): out of memory: can't find 1519616 bytes. aborting.」とか出て死んだりしてまだあやしい。
今のままじゃ出来ることがかなり限られるけれども、もうちょっと機能が増えればおもしろく遊べそうだし、何もJavaじゃなくても、JVM部分を捨てて別の言語の処理系を載せても楽しめるのではないだろうか。
取り敢えず、そういう事が出来るフレームワークがBSDLで公開されているのはいいね。