前書き
この記事は連載の一部となっています。連載インデックスは 【ILP 連載】自分で ILP を実装してみよう! をご覧下さい。
今回もややライトな記事になります。ライトでもいいからちょくちょくアウトプットしてノートを色々残していった方が Qiita 的にはいいのかなという感じでやっていきます。
今回は、 macOS 上で PPC64 をエミュレートして C# で big-endian の環境を構築してみた話 です。「え、何でそんなマニアックな事してるん?😅」 みたいな感じだと思うのですが(笑) 個人的なイメージですが、ILP は様々なコネクタが参加する可能性があります。中には、もしかしたら POWER 系の CPU を積んだサーバーでコネクタを走らせる人もいるかもしれません。サーバーの CPU にはあまり詳しくないですが、現状はほとんどの CPU が little-endian 系だと思うのでかなり可能性は低いと思いますが、万が一 big-endian の環境でコネクタを走らせる人がいて ILP のコードが big-endian の環境に対応していないコードだった場合、バグって正常に動かないという事が起こり得ます。
せっかく ILP に興味を持ってくれてわざわざコードをインストールしてくれたのにバグって動かないとなったら、残念ですよね…😢 これは避けたいです。その様な残念な状況にならないために、 自分のコードが big-endian にきちんと対応している事を確認しておきたい、という意図です。
なお、C# の場合、アーキテクチャ次第、Java は環境によらず big-endian 固定、node.js はアーキテクチャ次第、という事の様です。つまり Quilt の ILP のコードは big-endian のみ対応しておけば良い、interledgerjs に関しては両方のアーキテクチャでテストしておく必要があるという感じの様ですね。
やる事
さて、やる事としては、こんなイメージです。
- Mac 上に QEMU をインストールする
- QEMU 上に CentOS をインストールする
- CentOS 上に mono をインストールする
- C# のコードを mcs でコンパイルし、テストする
まず、大前提として私は普段 Mac Pro で開発しています。つまり Intel Xeon E5 / macOS 10.13 の組み合わせで、little-endian ですので、big-endian の環境のテストをする事ができません。知り合いに QEMU というものがあるよ、と教えてもらいました。これは CPU 自体をエミュレートしてくれるとんでもないアプリで(笑) little-endian の環境であっても、big-endian の環境を構築する事が可能です。助かるぅ!
今回は QEMU 上に PPC64 に対応していて mono も動きそうなものとして、CentOS をインストールしてみる事にしました。ただし、PPC64 上で動く CentOS 用の mono は標準では提供されていませんので、自力でコンパイルする必要があります。
Mac 上に QEMU をインストールする
これは簡単です。以下の様にします(brew が入っていない人はインストールする必要があります)。
$ brew install qemu
インストール自体はこれで完了です。
QEMU 上に CentOS をインストールする
次に CentOS をインストールするための仮想のディスクを作成します。
$ qemu-img create -f raw CentOS-7.img 20G
容量は結構適当です。ただ、5GB とかだと足りなくなりますので注意して下さい。次に、インストールしたい CentOS の iso ファイルをダウンロードします。PPC64 は x86_64 などと違って主流ではない、という意味で AltArch と呼ばれている様です。ダウンロードページなどから AltArch(Alternative Architecture の略と思われる)を辿ってダウンロードして下さい。その際、ミラーサイトを使う事を強くオススメします。本家からダウンロードしようとするとめちゃくちゃ遅いです(笑) 例えば ftp.yz.yamagata-u.ac.jp とかがいいかもしれません。ppc64le は little-endian 用なのでこちらを選ばない様に気を付けます。
$ curl -O http://ftp.yz.yamagata-u.ac.jp/pub/linux/centos-altarch/7/isos/ppc64/CentOS-7-AltArch-ppc64-Everything-1708.iso
では起動してみます。
$ qemu-system-ppc64 -cdrom CentOS-7-AltArch-ppc64-Everything-1708.iso -boot order=d -drive file=CentOS-7.img,format=raw -m 4096 -net nic -net user
ここで、-m 4096
で割り当てているメモリの 4096 は適当ですが、ある程度は割り当てておかないとインストールに失敗するので注意して下さい。私はこれで数時間ハマりました。おそらく起動するのに結構時間がかかりますし、インストール自体も数時間かかるのですが、それでも一応インストールはできますので頑張って下さい(ぉぃ)。
インストールできたら、普通に起動します。
$ qemu-system-ppc64 -drive file=CentOS-7.img,format=raw -m 4096 -net nic -net user,hostfwd=tcp::2022-:22
こんな感じになっているはずです。ちゃんと ppc64 で起動できているのが分かりますね。hostfwd を指定しておくと、QEMU 上で起動されているマシンと自分のマシンのポートを紐づけておけるので ssh で接続できる様にできます。便利なので今回は 2022 に 22 をマッピングしておきます。
$ ssh localhost -p 2022 -l dora-gt
この様に接続できる様になります。ただ、エミュレータが遅すぎて、接続に失敗する事があります。
$ ssh localhost -p 2022 -l dora-gt
ssh_exchange_identification: read: Connection reset by peer
この様になっても、根気よくトライする事が必要です(笑) 諦めてはいけません。サンクコストとかいう言葉に騙されて挑戦し続けない人には成果は一生得られないのです。…冗談ですから本気にしないで下さいね(笑) 人生、諦めなければいけないものとそうでないものを見極める事が重要です(?)。
CentOS 上に mono をインストールする
さて、OS がインストールできたので mono をインストールしてみます。
$ curl -O https://download.mono-project.com/sources/mono/mono-5.10.1.12.tar.bz2
# 標準だと bz2 が伸張できなかったのでインストールする
$ sudo yum install bzip2
$ tar -xf mono-5.10.1.12.tar.bz2
$ PREFIX=/usr/local
$ VERSION=5.10.1.12
$ cd mono-$VERSION
# コンパイルするために必要なのでインストールする
$ sudo yum install gcc
$ sudo yum install gcc-c++
# make するのに 19 時間かかりました(笑)
$ ./configure --prefix=$PREFIX
$ make
# こちらも 2 時間くらいかかりました
$ sudo make install
やはりエミュレーションなので時間が相当かかります。コンパイル&インストールで 21 時間かかるという(笑) 心配になりますが動いているので信じて待つしかありません。部下の事を信じるのも上司の仕事です(??)。
$ mono --version
Mono JIT compiler version 5.10.1.12 (tarball 2018年 3月 24日 土曜日 12:35:22 JST)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
TLS: __thread
SIGSEGV: altstack
Notifications: epoll
Architecture: ppc
Disabled: none
Misc: softdebug
Interpreter: yes
GC: sgen (concurrent by default)
できたー!!ほぼ1日待ってちゃんと動くと、感動しますね😂 mono の開発者のみなさん、ありがとう…。
C# のコードを mcs でコンパイルし、テストする
さて、では実際に C# のコードを書いて実行してみましょう。
using System;
namespace EndiannessTest
{
public class Test
{
public static void Main(string[] args)
{
Console.WriteLine(string.Format("BitConverter.IsLittleEndian: {0}", BitConverter.IsLittleEndian));
ulong ulongValue = 12345678901234567890;
byte[] ulongBytes = BitConverter.GetBytes(ulongValue);
Console.WriteLine(string.Format("ulong: {0}, bytes: {1}", ulongValue, BitConverter.ToString(ulongBytes)));
}
}
}
$ mcs Test.cs
$ mono Test.exe
# Intel Xeon E5 で実行
BitConverter.IsLittleEndian: True
ulong: 12345678901234567890, bytes: D2-0A-1F-EB-8C-A9-54-AB
# PPC64 で実行
BitConverter.IsLittleEndian: False
ulong: 12345678901234567890, bytes: AB-54-A9-8C-EB-1F-0A-D2
はい、PPC64 の環境で実行すると IsLittleEndian が False、つまり big-endian や! きちんと bytes も逆転しているのが確認できますね。これにきちんと対応しておかないと、バグる事になります。何故かと言うと、ILP の OER エンコーディングは big-endian である事が前提だからです。
終わり
という訳で、big-endian の環境を構築する事ができました。これから、これを使って自分のコードが正しい事を確認していきたいと思います。
それではみなさん、よい ILP ライフを!