セキュリティ&プログラミングキャラバン東京2009

午前は寝坊でほとんど聞けず。OS30分ライブも面白かったけど、あれは観ながら盛上がるもの。というか「30日でできる! OS自作入門」は買ったのに積読になっていますごめんなさいごめんなさい。

その後にあった、RubyをネタにしたVMGCの話がすごい面白かったのでメモを残しておく。講師はYARVのささだこういちさん。内容が間違っていたら、残念ながらそれは私が原因だ。

Ruby処理

VMの話。VMのデモを通じて、もっとも原始的なVMが腑に落ちた。

  • 処理系は何をしているか。「プログラムを読む」「読んだプログラムを実行する」。これだけ。あまり難しく考えてはいけない。
  • もう少しいうと、プログラムを読む部分はコンパイラという名前がついており、「パーサ」機能と「コンパイラ」機能が含まれる。実行する部分は評価器という名前がついており、「バイトコードの実行」「オブジェクト管理(GCとか)」「組込クラス・メソッド」の機能を持つ。
  • VMにはStack MachineとRegister Machineの2種類がある。YARVはStack Machine。
  • 仮想マシンについて。なぜ作るかといえば、CPU独立、マシン依存部分を吸収して移植性を含めた開発効率を上げるため。開発効率は実行速度とトレードオフになる。両方が最高に優れたVMは作れないので、どちらをどれだけ優先するかのバランスが大事。お客さんの意見によってバランスは変わる。Rubyでは、まつもとゆきひろさんがRubyを作るときにPeal置換を目指したため、文字列処理に重きを置いてきて、それだけならVMはなくてもよかったが、利用場面が広がってVMが必要になってきた。
  • バランスの例としては、MacRubyMacに特化した環境でのみ動作すればいいので、Mac独自の機能をたくさん使っている。64bitマシン独自の機能をつかっているので、「それじゃ32bit環境では動かない」とMacRubyのメンテナに言ったら「OS Xは64bitだからそれで問題ない」と言われた。ある意味羨ましい。
  • 仮想マシンの命令セットについて。Javaや.NETはStack MachineのVMYARVを作るときになぜStack Machineを選んだかというと、Javaや.NETなどで参考資料が充実していたから。それぞれのVMの特徴は
    • Stack Machine:命令セットが少ないのでコンパイル後のサイズが小さくなる。ネットワークでやり取りしたり、組込機器に載せたりするときに有利になることを狙ってJava VMはStack Machineを選択したのではないか。
    • Register Machine:命令セットが多くなるが、その分簡単に書ける。こちらのほうがコンパイル後のjumpが少なくなり、実行時のオーバーヘッドを減らせる。jumpの多寡は処理速度への影響が大きい。
  • Rubyで書いたStack型のVMと、Register型のVMの実行例をデモ。Stack型はpushとpopにいくつかの命令を追加しただけで、それをwhileしているだけ。Register型はそこがもう少し複雑になるが、whileしているのは同じ。ちょっとしたVMでも命令セット数が倍以上違う。
  • VMといってもやっていることは単純。ただしその単純なことを積重ねていくと出来上がったものは大分変わってくるので、お客さんの要望によって選択は慎重に行なう必要がある。

お勧めの本は「コンパイラとバーチャルマシン」だそうです。とりあえず帰りに紀伊国屋書店で買いました。積読にならないように気をつけないといけない。

Rubyメモリ管理

情報処理学会で発表したもの。GC(ガーベージコレクション))の知識があることが前提なので知らない人は諦めて、といいつつGCの説明もはさんでくれる親切設計。私はたまたま「GCアルゴリズム詳細解説」というサイトで勉強したことがあったので、ある程度ついていけた。以下ではGCの説明は省略するので、Mark&Sweepの図くらいは眺めておくと、メモが読みやすいかも。

  • RubyインタプリタC言語で実装されており、VMGCは保守的Mark&Sweep。最近はRubyの利用用途が広がって処理速度の向上要求が強いので、Rubyを1.9.1から1.9.2にバージョンアップするときにGCも改良して速度向上するように実装中。
  • RubyGCRubyオブジェクトは5wordの固定長であらわされている(文字列などはmalloc()/free()で管理)。これらオブジェクトは、RootオブジェクトはStackに積まれるが、そこから派生するオブジェクトはHeapに確保され、Heap-Slotsで辿れるようになっている。
  • 勝手に実装していいなら苦労はない。すでに多数利用されているRubyのメモリ管理を変更するにはいくつかの制限がある。
    • ソースレベル、バイナリレベル両方での互換性厳守。メジャーバージョンアップのときには互換性を破ってもいい(ただし適合しないプログラムはコンパイルエラーとなるようにする)が、今回はマイナーバージョンアップなので、互換性を破れない。GCだけの問題に見えるが、GCは処理系全体に影響が及ぶので慎重にならないといけない。以前、GCを取りかえ可能にするという案が提案されたことがあって互換性について心配していたが、却下されていた。
    • オブジェクトの移動ができない。つまりコンパクションやコピーを伴うGCを採用できない。
    • ライトバリアもできない。なので世代別GCやインクリメンタルGCを採用できない。
  • GC改善の取っ掛かり。最初はGCのオーバーヘッドが何によって影響されるかを考えた。Heapサイズを大きくすればトータルのGC実行時間は下げられる(当たり前)。ただしHeapサイズを単に大きくすると、他のアプリのHeapメモリが足りなくなる、オブジェクトフラグメンテーションが発生する(コンパクションは禁止)、メモリアクセスの局所性が失われる、といった問題が発生する。
  • 他のアプリのHeapメモリ対策。他プロセスと協調してヒープサイズを自動調整することを考えた。適切なヒープサイズを確認する方法としてOSに訊く方法があるが、いろいろあってそれが本当に適切なサイズなのかはわからない。なのでRubyに対してメモリのページアウトが発生するのを監視することで確保しすぎと判断することにしてみた。
  • フラグメンテーション対策。Heap-Slotsの個々のSlotサイズ(?)をそれまでの16KBから4KBにした。またオブジェクトの寿命を予測して、短寿命オブジェクトには広いHeapを用意することを考えた。ただしこれにはオブジェクトの寿命予測が大事。
  • Sweepの実行時間削減。スキップされた。
  • それらをどうやって実装したか。Heap-Slotsの管理方法にArenaを導入(ちなみにこの実装はセキュリティ&プログラミングキャンプ中に参加者が実装した)。最初に128KBをHeapからまとめて予約して、そこから必要に応じて4KBずつ確保するようにした。Heapの予約は全部のOSに対応しているわけではないが、ほとんどのOSで対応しているのでよしとした。が、おもったよりもOS側でのオーバーヘッドが大きくて効果は微妙。
  • マークカウンタも実装。GC実行時に、Heap-SlotsのSlot(?)ごとにマークしたオブジェクトの数を数えて、カウンタがゼロのSlotでは一括開放を行なうようにした。これは結構効果があった。
  • あと何とかかんとか。実装完了までにあと何箇所か未完成の箇所が残っている。言語系で処理を速くするためには、OSやプロセスの知識も必要、云々。

それ以外

ライトニングトークは省略。最後のフリーディスカッションでひとつだけ妙に記憶に残ったやり取り。確か「使える技術者とはどのようなものか、そういう技術者になるにはどのような勉強をすればいいか」という質問で、講師のひとりが「IT以外で一生懸命勉強しておくと、それはITを考えるときにも役立ちます」という回答をしていた。

あと、これがキャンプもキャラバンも無料というのは素晴らしいのですが、事業仕分けの影響で、セキュリティ&プログラミングキャンプ自体が来年も実施されるかどうか見通しがまだ立っていないみたいです。幸いにして仕分けの網をくぐることができて来年も実施されるようなら、応募資格のある人は全力で応募しましょう。「何でもやります」「やったことがないけど興味があります」という人より「こういうことをやってきました」「こういうことがやりたいです」という人を採用するとのことです。私はすでに応募資格はないです。というか参加者はみんな若かったな。講師をのぞけば、自分はたぶん上位5%くらいの年齢だったんじゃないだろうか。

このキャンプやキャラバンの中心人物であろう人に感謝の念をこめて、トラックバック

文字セットと文字符号化とエンディアン

せっかくだから書くことで覚える。

文字を集めて番号を振ったもの。ASCIIとか、JIS X 0208とか、Unicodeとか。文字コードの種類によって、使える文字と、その文字に割振られた番号とか違う。

文字コードに振られた番号を、1と0のバイナリで表す場合の書式を決めたもの。ISO-2022-XXXXとか、EUC-XXXXとか、UTF-XXXXとか。1バイト用のエンコーディングと、2バイト用のエンコーディングがある。

エンコーディングされた番号を、メモリ上でどうやって表現するか決めたもの。多バイトのエンコーディングを扱う際に問題になる。上の桁を若いアドレスに割当てるのがビッグエンディアン。下の桁を若いアドレスに割当てるのがリトルエンディアン。これは基本的にはCPUによって決まっている(PowerPC系だとビックエンディアンIntel系だとリトルエンディアン、など)が、VMを使う言語によっては、VMエンディアンを統一している場合もある(JavaVMはビッグエンディアン)。

参考にしたのはこちら。
おつあり UnicodeとUTF-8の違いは?
Wikipedia エンディアン
Wikipedia 文字コード

CentOSを動かす

半年も間が開いてしまった。

最近はPCが安いので、Atomベアボーンを買ってきてLinuxを入れてみた。サーバの練習をしたいのでCentOSを入れてみる。パーティションはデフォルトで。テキストでいろいろ操作できるようになると格好よいのでは、という理由で最初はオプションを全部外してみた。

と思ったら、ネットワークカードすら認識しなかった。ちょっとわからないので改めてインストールを試す。

追記:
同じようなことを考えていた人がメモを残してくれていたので、それを参考にドライバをインストールしました。感謝。書き忘れていたけど、マシンはValore ION 330(DVDモデル)、OSはCentOSの5.3です。

ネットワークドライバのインストール

このマシンはマザーボードがASRockのAMCP7A-IONなので(中を開けると型番が印刷されている)、ASRockのサイトからドライバをダウンロードします。

私の場合は32bit版をインストールしていますので、上のタブ?みたいなDownloadsから /linux/elrepo/el5/i386/RPMS と進み、

  • kmod-forcedeth-0.62-1.25.2.el5.elrepo.i686.rpm
  • kmod-forcedeth-PAE-0.62-1.25.2.el5.elrepo.i686.rpm
  • kmod-forcedeth-xen-0.62-1.25.2.el5.elrepo.i686.rpm

のうち、xen仮想マシンで関係ないはずなので、最初の2つをダウンロードします。

これを何かのメディアに保管してRPMで展開します。私はUSBメモリが行方不明だったので、CD-ROMを使いました。焼いたCDをドライブにセットして、勝手に認識してくれるところをわざわざコマンドでマウントしてからインストールしました。

# mount /dev/cdrom /mnt/cdrom
# rpm -ivh /mnt/cdrom/kmod-forcedeth-0.62-1.25.2.el5.elrepo.i686.rpm

PAEはとりあえず関係なさそうなので放置。

これで再起動してifconfigを実行して、eth0が出てきたらインストール完了。

グラフィックドライバのインストール

一緒に掲載されていたグラフィックドライバのインストールもついでにやってみる。

NVIDIAのサイトからIONのグラフィックドライバ(Linux 32bit版)をダウンロードする。私はネットワークドライバをインストールする前にWindowsでダウンロードしたのだけど、テキストが表示されてしまうので右クリックで保管した。

ここから先がややこしい。これはカーネルとインターフェースをあわせるために、カーネルも一緒にビルドしないといけない。その準備として、gccツール一式をインストールしておく。

# yum install gcc* compat-gcc* compat-glibc* compat-lib*

GUIで作業したままでビルドしようとすると、Xwindowで作業するなと怒られる。なのでCUIに移行してあらためてログイン。

# /sbin/init 3

で、上手くいくと思ったら今度はソースがない、って怒られたので、kernel-develをインストール

で上手くいくと思ったらまだ駄目なので、後でやりなおす。

ThunderbirdからIMAPでGmailを利用する方法

いつも忘れてしまうのでメモ。最近はGmail IMAP Account Setupという拡張ツールも出ているみたいだけど、これくらいは自力でやってみたい。

以下の内容はすぐに忘れる脳みそのためのメモ:Thunderbird で Gmail を利用する - IMAP を使ってを参考にしました。

参考っていっても丸写しに近くなったのですが、書いて覚える(忘れたときに検索して思い出す)のも目的のひとつなので、許してください。

Gmail側の設定

  • [設定] -> [メール転送と POP/IMAP 設定]を開いてIMAPを有効にする。変更を保存するのを忘れずに。


これだけ。

Thunderbird側の設定

こっちが少し面倒。

  • [ツール] -> [アカウント設定]で設定画面を開く。
  • 左下の[アカウントを追加]ボタンをクリック。
  • アカウントの種類は、[メールアカウント]を選択することに注意。ここでGmailを選ぶとPOPでのアクセスになる。
  • 差出人情報は、[あなたの名前]には一般表示させたい名前を、[メールアドレス]欄にはGmailのメールアドレスを入力する。
  • サーバ情報は、[サーバの種類]にはIMAPを、メール受信サーバにはを入力する。
  • ユーザ名は、[受信サーバのユーザ名]にGmailのメールアドレスを入力する。
  • アカウント名は、Thunderbird上の表記だけなので好きに付ける。

これで完了すればまずはアカウントの作成が完了。でもまだ終わらない。

  • 作成したアカウント設定を開いて、[送信(SMTP)サーバ]でGmailを選択する。ここでまだ選択肢にGmailがなかったら後の項目を参照に送信サーバ設定を作ってから、あらためて選択する。
  • 作成したアカウントの[サーバ設定]を開いて、[ポート]が993番になっていることを確認。それとセキュリティ設定も[SSLを使用する]を選択しておく。
  • 一番下の[送信(SMTP)サーバ]を開いて、Gmailのアカウントの編集を選んで、[ポート番号]が587番になっていることを確認する。
  • [送信(SMTP)サーバ]でGmailのアカウントがない場合は、[追加]を選んで以下を入力する。
    • [説明]は何でもOK。
    • [サーバ名]はを入力。
    • [ポート番号]は587を入力。
    • [ユーザ名とパスワードを使用する]にチェック。
    • [ユーザ名]はGmailのメールアドレスを入力。
    • [保護された接続を使用する]はを選択。

最後にOKで終わることを忘れずに。

あとは実際に送受信を確認する。