3/27 (土)
[Linux] RHEL5.4 に pfmon2 用のカーネルパッチをあてようとして失敗
RedHat Enterprise Linux 5.4 のカーネルに pfmon 用のカーネルパッチをあてようと試行錯誤していたが、結局失敗したなり。
ia64 アーキの Linux カーネルは pfmon2 のカーネルパッチをメインストリームで取り込んでいるため、libpfm と pfmon コマンドを用意すれば使えるようになる。 しかしi386 や x86_64 にはカーネルにパッチをあてる必要がある。
pfmon のページにはメインストリームに則したカーネルパッチがバージョン毎に提供されているのだが、RHEL5 系は 2.6.18 がベースだが RedHat の独自パッチが大量にあたっているため、メインストリームのバージョンとは全然一致しないものになっている。 pfmon カーネルパッチの追加したシステムコールの番号が RedHat のパッチで追加したものと衝突した時点で諦めモード。
一方、RHEL5.4 for x86_64 にはなぜか pfmon-3.2-0.060926.5.el5 と libpfm-3.2-0.060926.4.el5 のパッケージが入っている。 これ使いようがないじゃん。 それともどっかで RHEL 系用のカーネルパッチを配っているのかしら?
3/23 (火)
[Java] invokedynamic 命令についての覚え書き (1)
Java Virtual Machine (Java VM) の仕様に JSR 292 Expert Group で検討された invokedynamic 命令が追加され、JavaSE 7 から導入されるようだ。 invokedynamic 命令がなぜ必要になったかとか、Core Reflection API との違いなどをメモしてゆくつもり。
バイトコードの形式
まず Java VM はバイトコードと呼ばれる命令形式をもっている (Java VM Specification ただし 2nd、maintenance information)。 Java VM バイトコードの特徴をまとめると
- 命令は可変長のバイトサイズを持つ。先頭1バイトがオペコードになっており、これが命令種類をあらわす。
- 命令中でクラス名やメソッド名などの指定が必要になった場合、クラスファイルの中のconstant pool(定数プール)と呼ばれる定数データ部分のインデックスが指定される。
- レジスタがないスタックマシーンである。命令毎にスタックにプッシュ・ポップが決まっている。
メソッド呼び出しを実行する命令は、invokedynamic 導入前は4つ存在した。
- invokestatic (static メソッド呼び出し用。3バイト命令)
- invokespecial (実質イニシャライザ呼び出し専用。3バイト命令)
- invokevirtual (仮想メソッド呼び出し用。通常のメソッド呼び出しはこれ。3バイト命令)
- invokeinterface (仮想メソッド呼び出し用。通常のメソッド呼び出しはこれ。5バイト命令)
invokeinterface 以外は 3 バイト命令で、いずれも先頭1バイトがオペコードをあらわし、残りの2バイトが呼び出したいメソッドディスクリプタを指す定数プールの番号を指している。 レシーバーと呼び出し引数は事前にスタックに積んでおき、invoke* 命令が呼ばれるとスタックから一斉にポップされる。 メソッドの戻り値は invoke* 命令実行後にスタックにプッシュされる。
invokevirtual 命令と invokeinterface 命令の違い
invokevirtual 命令と invokeinterface 命令の違いは Java のソースコードからは見え辛い。 クラス変数からメンバを呼び出した場合は invokevirtual が、インターフェイス変数からメンバを呼び出した場合は invokeinterface が使われる。
この違いを説明するためには、Java のクラスの継承の実装を考える必要がある。 Java のクラスは単一継承なので基底クラス(実際は java.lang.Object) から積み上がるようにフィールドとメソッドが増えて行く。 例えば Base クラスと Derived クラスがあった場合、
import java.io.*; interface Interface { public void i1(); public void i2(); } class Base implements Interface { public void m1() { System.out.println("Base.m1\n"); } public void i2() { System.out.println("Base.i2\n"); } public void i1() { System.out.println("Base.i1\n"); } } class Derived extends Base { public void m1() { System.out.println("Derived.m1\n"); } public void i1() { System.out.println("Derived.i1\n"); } public void m2() { System.out.println("Derived.i1\n"); } public static void main(String[] args) { Base b = new Derived(); b.m1(); // invokevirtual が使われる Interface i = b; i.i1(); // invokeinterface が使われる } }
(Object クラスのことを無視すると) Base クラスは 3 個のメソッドがあり、Derived クラスは 1 個メンバが増えて 4 個のメソッドがある。 クラスのメソッドは C++ 風で言うと VTBL で管理されており、Java VM の内部では以下のような VTBL が作られることになる。
- クラスは親クラスから VTBL を受け継ぎ、それに登場しないシグネチャがあれば VTBL を追加してゆく。 Derived クラスは Base クラスを継承したが m2() というメソッドは Base にはなかったので #4 のメンバとして登録された。
- 追加するメソッドは Java VM の内部ルールに従い VTBL に追加される。 JVM を普通に実装すればメソッドの登場順に追加してゆくであろう。 Base クラスでは m1() → i2() → i1() と登場したのでこの順に並べている。 i2() と i1() を逆順にした点に注意。
この順序は javac の中でクラスファイルが作られるときにクラス単位で序数が仮決めされるが、本当の番号はクラスがロードされた時に動的に決まる。 そのため Derived クラスをコンパイルした後に、Base クラスを再コンパイルしてメソッドを増やすなどは認められている。
typedef void (*method_call_t)(); method_call_t Base_vtbl[3] = { Base_m1_method, // #1 Base_i2_method, // #2 Base_i1_method, // #3 }; method_call_t Derived_vtbl[4] = { Derived_m1_method, // #1 Base_i2_method, // #2 Derived_i1_method, // #3 Derived_m2_method, // #4 };
このようにメソッドの番号は、クラスロード時に確定してしまうため invokevirtual 命令が呼び出しメソッドを「Base.m1()
を呼ぶ」と名前で記憶していても、実行時には「クラスの VTBL の1番目(#1)のメソッドを呼ぶ」というように読み替えることができる。
そのため強力な JIT コンパイラを持っていない初期の JavaVM の中には、invokevirtual Base.m1()
をバイトコード命令の空きオペコードを使った疑似コードに書き換えて fast-invokevirtual #1
に最適化することが可能であった。
一方で interface はそうはいかない。 サンプル中の Interface は i1() と i2() を持っているが、Base クラスの中では i1() は #3 で i2() は #2 になっている。 別の Another クラスが Interface を実装したとしても、i1() と i2() の位置番号は変わってしまっている。
class Another implements Interface { public void i1() { System.out.println("Another.i1\n"); } public void i2() { System.out.println("Another.i2\n"); } }
method_call_t Another_vtbl[2] = { Another_i1_method, // #1 Another_i2_method, // #2 };
しかし Base クラスも Another クラスも Interface 変数で受けることができるので、それぞれの i1() と i2() が呼び出せる必要がある。 この際には invokevirtual よりも複雑な検索が必要になる。
invokeinterface が 5 バイトで invokevirtual よりも 2 バイト長い命令と決められているのは、高速化のためのバイトコードの書き換えに余分なメモリスペースが必要であろう… と想定して設計されたと思われる。
3/19 (金)
手作り居酒屋 甘太郎 (ぐるなび)
私と一緒に研究所から出向の形で新横浜に送られたO部長は一足先に川崎に戻れることになった。 その送別会を新横浜で行なう。
最初10名いた研究所のメンバーは私も含めて3人だけになる。 当初は去年の6月までという話だったのだが、開発方針の大幅変更で1年伸びて「今のところ」は今年の8月までお勤めでごんす。
O部長はアレの開発の顛末を見ないで済むのだから幸せだよなぁ。 心なしか表情も朗らか。
3/16 (火)
[Linux] x86-64 の TF フラグを自由に使いたい
x86-64 のシングルステップ実行は、RFLAGS の中のトーレスフラグ(TF)ビットを立てることで実現できる。 TF フラグが立った状態で、1命令の完了後に #DB 例外が発生し処理がトラップハンドラに移ることになる。
x86-64/Linux カーネルは、この TF フラグを ptrace
の PTRACE_SINGLESTEP
の実現のために使用している。
これが設定されたターゲットプロセスは1命令実行すると SIGRAP シグナルが送信される。
ところで私は仮に「メモリ領域にその属性を付けると、メモリアクセス命令が完了後に必ず SIGTRAP シグナルがあがる」ようなきのを実現したいと思って、x86-64/Linux カーネルを弄って do_page_fault
関数内で条件により regs->eflags
に TF_MASK
を追加するというコードを書いたのだが、ユーザランドに戻った後にメモリアクセス命令を再実行しても #DB 例外が上がってこないという変な状況に陥った。
常に #DB 例外が上がってこないのではなく、上がってきたこなかったりする。
現象の説明をいろいろ考える。
- x86-64 CPU の TF フラグによる #DB 例外は、条件によって命令が完了しても発生しないことがある。
→ さすがにこれはないだろう。非同期割り込みと #DB 例外は #DB 例外が優先だし。 do_page_fault
の呼び出し元から iret までの間にあるコードが、regs->eflags
からTF_MASK
を除去してしまう。
→ む〜。- 非同期割り込みの事象の延長戦上で
regs->eflags
からTF_MASK
クリアされてしまう。
→ 非同期シグナルの発生時には
調査の続きは明日やろう。
追記:3/17
勘違いだった。
regs->eflags
に TF_MASK
を加えてレジュームすると、次にユーザランドに処理が移って命令処理を再開すると #DB 例外は必ず出ている。
#DB 例外ハンドラ内で取りこぼしているだけだった。
3/14 (日)
JR横須賀線武蔵小杉駅開通
正確にはJR横須賀線の武蔵小杉駅が開通した。 これで武蔵小杉駅には東急東横線(など)、JR南武線、JR横須賀線の3路線を抱えることになった。 横須賀線は品川/新橋経由で東京駅までが20分になるので、秋葉原・上野にゆくのであればこの路線が最短になった。
ただJR南武線とJR横須賀線のプラットフォームは大きく離れており、地下の連絡通路はなんと420メートル。
[Food] 魁龍@台場ラーメンパーク
とりあえず記念乗車ということで、武蔵小杉から新橋に出ました。 汐留へのアクセスが便利になって、いいのやら、悪いのやら。 そのままゆりかもめに乗って台場まで行きます。
というのもお台場ラーメンパークの第4弾の最終日。 食べ損なっていた「魁龍」のラーメンを食べてきます。
今日見た正直すぎる広告
ゆりかもめの車内で見たブッカーズという広告。
ケータイで本を読むなんて
冗談じゃない!と
思っていました。
私たちがやるまでは。
東京都書店商業組合
まあ自分達以外がやるのは「冗談じゃない!」よね。
3/10 (水)
次の 10 年に流行るコンピューティングについて考えている
普段から「次の10年でどういう技術が出てくるか」を考えている。 無論、自分の思ったような方向に世界は進まない。 自分の思うように進んでいっているのは GPS のような位置情報を使ったロケーションサービスの発展ぐらいだ。
私が10年ぐらい前から「次に流行るのはこれだ!」と思っているのは「情報を自己消費する」コンピューテイング技術だ。 というのもインターネットの商業利用解禁以降、情報は圧倒的な速度で爆発し、それを「収集」するために様々な技術が開発されてきた。 掲示板や SNS や検索エンジンでも RSS でもソーシャルブックマークや Twitter もみんなその方向。 だが情報を見たり観たり聞いたり眺めたりするのは相変わらず生身の人間である。 もうそろそろそうい「消費」行動をコンピュータ自身が行なってくれるようにならないだろうか?
これはコンピュータ上に自分の過去の検索結果、アクセス履歴、コメント等などの書き込み情報をもとに、その判断を模すことが可能なエージェントを作り出すというアイデアだ。 エージェントには自分の代わりに情報の「消費」して取捨選択をしてもらう。 例えば、
- 自分の巡回経路を廻って代理ブックマーク。
- 自分が注意を引きそうなページだけピックアップ。
- 本や映画を処理して自分が興味を持ちそうな記述・シーンを固めて要約。
- 簡単な文章なら代理でコメント投稿
- ゲームも代わりにプレイしてくれて、要所要所の見どころに来た時だけ自分を呼びとめてくれる。
エージェントは平行処理も可能であろうから、大量の情報を同時に処理できる。 人間は2ちゃんねるの掲示板を全部読んでいる暇はないが、エージェントが「自分だったら気になるだろう」ところだけを集めたメモを作ってくれる。 誰もが自分用のエシュロンを皆が持っている状態だ。
そういう情報を消費する技術があれば「べき乗則」を打ち破れると思っている。 現実の世界はベストセラーとかホットリンクとか参照数が多い情報が上位に位置付けられ、そうでない情報はリーチされることすらない。 しかし自分と同じ感覚を持ったエージェントが世界中の情報を自分の代わりにいったん「咀嚼」してくれるのなら、派手な宣伝をしているからといって TV ドラマにチャンネルを合わせてみたり、他人の評価を信じてベストセラー本を買ったりすることもなくなるだろう。
3/9 (火)
[Linux] crash コマンドの extension を書きませう
Linux カーネルのクラッシュダンプを解析していると、コマンドライン操作だけではどうにもならないようで、エクステンションを開発する必要が出てくる。 というか出てきた。
crash のソースコードに含まれている echo.c と dminfo.c を参考に自作のエクステンションの練習中。 もう少し理解できればこの日記を加筆する予定。
リンク
3/7 (日)
PC の世界から遠くはなれて
自宅 PC の Windows2000 のサポート期限が迫っているので、早急に環境を切り替える必要がある。 もちろん今のパソコンに新しい Windows を上書きしてしまうと、失敗した時に大変なので、新しい PC をもう一台組んでデータの移行を行なおうと考える。
というわけで新しい PC を新調するために横浜に出かける。 横浜の DOS/V パラダイスで展示物件を見るのだがカタログスペックが何を意味しているのか分からねぇ。
いや流石に Intel CPU の型番は分かるのですよ。
ですがビデオカードの良し悪しとか、Serial ATA の種類とかが分からない orz
マザーボードのスロットには未知らぬものが並んでいるし ATX ってどこいったの。
ちょっと勉強してから出直すことにします。
3/4 (木)
ヴィヴァルディ
Google のトップページが 「アントニオ ヴィヴァルディの誕生日」になっているのを見て、中学校の音楽教師が授業中に言っていた与太話を思い出した。
与太話というのはヴィヴァルディのエピソードなのだが、
ヴィヴァルディは永らく忘れられた作曲家で(これは本当) バロック音楽は J.S.バッハから始まると考えられていた。 (だから音楽室にある作曲家の肖像画列伝もバッハからはじまる)
ところが前世紀にイタリアの音楽家の集団イ・ムジチが生前ヴィヴァルディが住んでいたとある教会で埋もれていたヴィヴァルディ自筆譜を偶然にも発見。 200年以上の間、楽譜が捨てられることもなくふさわしい演奏家の手に渡ったのは奇跡だ。
という話だ。実際のところ、
- ヴィヴァルディは中世以降も完全に忘れ去られたわけではない。
- 1926 年にイタリアのトリノ大学図書館で多くの自筆譜が発見された。
- イ・ムジチ合奏団はヴィヴァルディの「四季」の演奏で有名だが、2. に関わっていたわけじゃない。
当時はインターネットもないし確認する手段もなく信じていたが、いま思い出すとこの音楽教師の言っていた話の半分ぐらいは与太だったよなぁ。 君が代イギリス人作曲説とか。
P.S.
そういえば亡母は四季の「夏」が陰気なのはヨーロッパの夏は蝿がブンブン飛んでうるさいのを表現しているからと言っていたなぁ。