2014年11月13日

また余計なことを… セキュア・ブート

日本に設置するために構築中のテレビジョン録画サーバのカーネルを 3.16.7 にアップデートし、放送キャプチュアカードのドライバを再コンパイルしてロードしようとしたらエラーになった。

root@h-rn312:~# insmod pt3_drv.ko
insmod: ERROR: could not insert module hello-1.ko: Required key not available

このドライバはクロス環境でビルドしているので、カーネルのバージョンを再確認して作り直してもダメ。

ぐぐってみたら、「Secure Boot」と言う機能で、要するにWindowsでマルウェアのドライバをインストールさせないために、Microsoftが「Windows 8 互換」を称するための必要条件としてBIOSで実現するように要求しているらしい。以前の 3.16.6 のカーネルではこの現象は見られなかったから、3.16.7から実装されたようだ。

マザーボード(ASUS)のBIOS 画面に行き、「Advanced」から「Secure Boot」を「Windows 8」から「それ以外のOS」に変更して回復した。

Secure Linux と言い、大多数のユーザには何の恩恵もない代わりに無用の混乱を引き起こすセキュア機能を黙ってデフォルト仕様にするのは如何なものだろうか?

2014年11月12日

GPT・EFI・RAID1 にHDDを追加

GPT

最近のPC用オペーレーティングシステムでは、大容量HDDに対応してGPT(GUID Partition Table)と言うパーティション方式が使われている。それまでのMBR(Master Boot Record)は1980年代のDOSの時代に設計されたもので、元々CHS(シリンダ、ヘッド、セクタ)というHDDの物理パラメタを使っていたが、のちにLBA(論理ブロックアドレス)を使うようになったものの、アドレスが32ビットなので2TiB(512バイト/セクタ×2^32セクタ)までしかディスク上の場所を記述できない(2TiBより大きいディスクにも使えるが、2TiBを超える部分はパーティションテーブルに記述できない)。また、MBRは元来4個のパーティションしか想定していなかったので、5個目以降のパーティションは「物理パーティション」の一つを更に複数の「論理パーティション」に分割するなどのつぎはぎで対応してきた。

GPTはMBRの制限を克服して、より柔軟性・拡張性のある新設計のパーティションシステムで、それぞれに誤り検出を備えた二重のパーティションテーブルをディスクの先頭と最後尾に記録して堅牢性を高め、事実上無制限のパーティション数(Widowsは128個に制限)、最大8ZiB(=2^73≒8×10^21)まで記述できるなど優れたパーティション方式である。

更に、GPTはMBRしか理解できない旧来のユーティリティによる事故を防ぐため、ディスクは依然としてMBRのパーティションデータを維持しており、MBRとして見るとGPT全体が一つのMBRパーティションに見えるなどの下方互換性も考慮してある。

EFI

EFI(UEFI、Unified Extensible Firmware Interface)はGPTと関連があり、PCの黎明期の資源(記憶容量)の節約が再重要課題であったMBRの時代に発案された先頭の512バイトセクタにブートローダを収めると言う現代の肥大したソフトウェアには完全に時代遅れとなったブートローダの格納方式を改め、GPTの最初のパーティション(FAT)にブートローダのファイルシステムを格納する方法である。512バイトのブートローダ笑ってはいけない。小生が最初扱ったCP/Mでは当時の単密度FDの先頭の128バイトセクタがブートローダだったから、それよりも4倍も大きかったのだ!

当然のことながら、EFIのブートシステムを使うためには、ハードウェア側のファームウェア・ブートローダがEFIブートローダを認識できなければならない。今日売られているPCのマザーボードはほぼ全てそのような仕様になっていると思われるが、MBRしか理解できないBIOSでも、MBRにもブートローダかかれており、このMBRブートローダがEFIのブートローダに引き継ぐようになっているので心配はない。

システム管理側から見ると、 ブートローダがファイルシステムとしてOSから見える空間にあることには大きな意味がある。ここで構築するRAIDシステムでも、その見通しのよさは利点となっている。

UUID

UUID(Universaly Unique IDentifier)またはGUID(Globally -)は、世界中で唯一絶対無二(と見做せる)のラベルであり、通常は計算機で発生された他数桁(代表的には16桁)の疑似乱数である。特定のオブジェクトを指定する方法として広い用途に使われており、例えばGITのバージョンなどにも使われるが、ここで関心があるのはディスクのUUID、特にパーティションのUUID。

パーティションには通常少なくとも二つのUUIDがつけられる。下は、今回のプロジェクトで生成したディスクとパーティションのUUID。

root@dvr3:~# blkid /dev/sd[ab]*
/dev/sda: PTUUID="32e9ab11-f4f9-429f-ba2a-2152b1e0c6de" PTTYPE="gpt"
/dev/sda1: SEC_TYPE="msdos" LABEL="BOOT_EFI" UUID="E6C8-88B4" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="9fa9a19d-edfa-4547-acdd-28a447847b8b"
/dev/sda2: UUID="e895041e-5b45-28e3-48f9-f6b95a9a7c1c" UUID_SUB="d37916fe-8ff6-ab8e-be2e-2be61513ce77" LABEL="dvr3:swap" TYPE="linux_raid_member" PARTUUID="f76213c2-06fa-4cce-8389-0dfd330cfb7c"
/dev/sda3: UUID="1e1befdc-8c1c-0df3-e6ff-fae3365c40dd" UUID_SUB="397fd84b-884d-ff9b-39a2-dee11c305fca" LABEL="dvr3:boot00" TYPE="linux_raid_member" PARTUUID="d574fbe7-7536-4f95-9192-d9383c682221"
/dev/sda4: UUID="ce81f465-4304-e57f-3ca5-cdc9e894e5b3" UUID_SUB="f65ff449-2a51-ac83-0a51-8bd871d01c2b" LABEL="dvr3:pv00" TYPE="linux_raid_member" PARTUUID="4ea40149-9100-41d5-98e5-ba7fa5a98512"
/dev/sdb: PTUUID="c4f133d4-4ae2-4fb7-bce5-f6eb9256887a" PTTYPE="gpt"
/dev/sdb1: SEC_TYPE="msdos" LABEL="BOOT_EFI" UUID="0085-40A4" TYPE="vfat" PARTLABEL="EFI System Partition" PARTUUID="93c20ab9-d77b-4ca8-8f58-f0127f7e0e82"
/dev/sdb2: UUID="e895041e-5b45-28e3-48f9-f6b95a9a7c1c" UUID_SUB="1ccd67c3-326c-2a42-8353-136e119fa651" LABEL="dvr3:swap" TYPE="linux_raid_member" PARTUUID="0df80d4a-86ad-49db-bc41-e79e5f546704"
/dev/sdb3: UUID="1e1befdc-8c1c-0df3-e6ff-fae3365c40dd" UUID_SUB="f54b5a8f-fd50-f281-0e13-5f2e76a8dc8c" LABEL="dvr3:boot00" TYPE="linux_raid_member" PARTUUID="44a25a01-c4a1-4b2b-a242-d7d93c949418"
/dev/sdb4: UUID="ce81f465-4304-e57f-3ca5-cdc9e894e5b3" UUID_SUB="47e97367-2f77-1d04-8402-91eb001271a2" LABEL="dvr3:pv00" TYPE="linux_raid_member" PARTUUID="b6842bbf-6018-430b-9177-fce9c1af7f94"

まず、各パーティションにはそのパーティションの内容に係わらず唯一絶対無二の「パーティションUUID」がつけられる。パーティションシステムはこのUUIDしか問題にしない。

/dev/sda1/dev/sdb1は問題のEFIパーティションだが、これらのパーティションにはmkfsでフォーマット・ファイシステムを作成したときに生成された、ファイルシステムごとのUUID(4バイト)が付けられている。4バイトという短さはFATのディスク構造の制限であり、ext[2-4]やBTRFSなどの近代的なファイルシステムは16バイトのUUIDがつけられる。

/dev/sda2/dev/sdb2は同じUUIDを持ち「唯一絶対無二」ではないが、これはこれらのパーティションが同じRAIDのメンバーであることを意味している。メンバー間の「個体認識」は異なるUUID_SUBで区別される。

実際には、上記の/dev/sda2/dev/sdb2からなるRAIDの上にLVMのUUIDがあり、その中の各ボリュームは固有のUUIDを持ち、更に各ボリューム上のファイルシステムのUUID、とそれぞれの階層でUUIDが付けられるが、ここでの関心はパーティションレベルまで。

OSのインスタレーション


FC20のインストーラAnacondaは、以前の版(FC5、FC13など)に比べて使いにくい。最近のこの手のソフトウェアの傾向として、Windowsを真似るだけでなく、「ソフトウェアはそれを使うユーザ自身より賢く、ユーザが究極的に何をしたいかをユーザ自身よりもよく知っている」という誤った思い込みで作られており、操作・結果の詳細をできるだけユーザに操作させない、あるいは少なくともユーザに見せないようになっており、ユーザの実行したいことがソフトウェアが想定するモデルに従わない場合は最悪の場合必要な操作が実行できない。FC20版のAnacondaもずいぶん困惑させられた。

加えて、AnacondaとEFIはRAIDと相性があまりよくない。後日、試行錯誤の末、気がついた点を述べる。

2014年9月29日

php.ini にタイムゾーンを設定する

PHPスクリプトの中で date() などの日時に関連した関数を使うと

It is not safe to rely on the system's timezone settings.
Please use the date.timezone setting, the TZ environment variable or the date_default_timezone_set() function.
In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.
We selected 'UTC' for now.

のような小うるさい警告がでる。

幾つかのサイトで /etc/php.ini にタイムゾーンを設定するように書かれている。
[Date]
; Defines the default timezone used by the date functions
date.timezone = Asia/Tokyo

しかしこれは上手く行かなかった。 で、上手く行ったのはこれ。
[Date]
; Defines the default timezone used by the date functions
date.timezone =
Japan

PHPのサイトのサポートされているタイムゾーンのリストの中に「Japan」はあるが、「Asia/Tokyo」はなかった。 ごちゃごちゃ言わずに、走行しているホストの現在のタイムゾーン(/etc/localtime)をデフォルトにすればよいのにと思う。

ちなみにPHPのバージョンは 5.5.16。

2014年9月27日

今度はUSBドングルが熱にやられた。

7年前に会社からもらったラップトップの延命策として802.11nのUSBドングルを挿したら目に見えて速くなった。

で、気をよくしていたら、どうもこのドングル経由のWiFiが不安定である。長くても数時間、短いときは10分程度で接続が切れてしまい、大抵の場合、デバイスをリセットしないと元に戻らない。

大変困っていた。Windowsのドライバが壊れているのかとか、ドングルが不良かと思い、 802.11acに買い換えることまで考えた。


で、ある時閃いた。それまで何となく気になっていた、ドングルを触ったときの熱さ。このラップトップはボディに左右にUSBポートがついていて、左側のポートにドングルを入れていたのだが、そのすぐ隣に電源コネクタがあり、従って多分中には電源回路があり、ここら辺がやたら熱くなるのだ。

右側のUSBポートにドングルを差し直したら、何日も安定して動作するようになった。また熱にやられた

詰まらない間違い。でも重要な影響だった。

2014年9月5日

ロックされているPDF文書にテキストを書き込む

無料のAdobe Readerを使ってPDF文書にテキストや署名を書き込むことができる。「書き込む」と言うのは正確ではないかもしれない。「描き込む」と言った方がよいかもしれない。PDFの所定の用紙に住所氏名などを電子的に記入できるので重宝する。

ところが、セキュリティとして書き込み禁止になっているPDFには描き込めない。

これを回避するのは比較的簡単。件のPDF文書を一度PDFファイルを生成する仮想プリンタで「印刷する」とできあがった新しいPDFファイルはセキュリティロックが解除されている。PDF出力プリンタも一応「法的に問題ないだろうな?」と確認してくることもある。

小生はWindows用PDF出力プリンタとして「Bullzip Printer」をずっと使っている。

2014年8月26日

ファイアウォールサーバがまた死にかけた

仕事場から家のLANをVPN経由でアクセスしているが、突然遮断された。仕事場のインターネットが全面的あるいは部分的に使えなくなることはそう珍しいことではないので静観していたら、約30分後に復旧した。ただし、LAN上のほとんどのホストの状態をPINGで監視しているメインサーバの報告によれば、少なくとも10分以上応答がなかったので電源再投入でリブートしたらしい。

サーバのフリーズはそう頻繁にあることではないが、全くない訳ではないから「数年に」一度の事故と考えたが、その後がよろしくない。約2時間後にまた同じ現象が起きた。今度も約30分後に生き返る。明らかに何かがおかしくなっているが、もうリモートで分かることはない。多分ハードウェアの異常だろうという推測は3回目の遮断ではっきりした。

このサーバは、その先代がやはりハードウェアの異常でフリーズを繰り返すようになった後、約1年前に新調したCeleronベースの比較的軽いもの。1年でまた壊れるとは納得が行かないが、ファイアウォールなのでこれがないと我が家のインターネット接続がすべて使えない。新しいものにリプレースするとして、症状から言ってCPUの不良は考えにくくマザーボードの可能性が高いからマザーボードだけ交換するか。しかしこのマザーボードはIntel 1155なのでだんだん入手し辛くなってきている。1150にするとCPUごと替えなければならない。Amazonからメイルオーダで買ったり実験を繰り返す時間的余裕はないから、即交換するとして余った部品をどこで再利用するか、などと考えながら帰宅を急ぐ。

帰宅途中にふと思いついたのは、電源故障もおおいに可能性があるというより、マザーボードの新しさから考えてそちらの可能性が高い。

帰宅してみると4回目の自動リブートでサーバは動作している。とりあえず自動リブートを禁止して様子を見る。

インターネットへのアクセスが遮断された。二階のサーバルームに行ってみる。サーバの電源が落ちている。決まった。電源ユニットに異常がある。

別のPCの構築作業中だったので、早速そちらから正常に動作することが確認されている電源ユニットを外して付け替える。復旧した!

取り外した件の電源をテストしてみる。ACを繋ぎ、得意の24(20)ピンATX電源コネクタの緑・黒ショートで電源を入れると、内部で電源の入るプスっと言う音が聞こえるものの、ファンが回らない。ファンを触ってみると非常に固くて回らない。ファンのベアリングがダメになっているようだ。

幸いなことに部品ストックに同じサイズのファンがあったので交換すると、動いた。件のサーバも順調に動作している。修理した電源ユニットを作業中の別のPCに装着しても平然と動作している。

結局、電源その物は生きていたが、ファンが死んでいたために数十分動作すると内部が温度上昇して保護回路が動作して電源を遮断し、その後我が家の監視システムが異常を検出して電源再投入するも内部が十分冷えるまでは再動作しなかったものと結論された。

教訓というほどではないが、トラブルシュートはセオリーどおりまず大元から疑うべし。そしてスペアの電源ユニットは必ず手元にいつも備蓄しておくべし、となった。

2014年7月26日

NFSトラブル Fedora 13/14 nobody:nobody

Fedora 13(FC13)上のVMPlayerで走らせているFedora 14(FC14)がブートしなくなった。原因は不明。ブーとの最中にカーネルがパニックを起こす。

 幸いなことにすべてのバックアップが揃っているので、FC14のイメージをVMPayerで再インストールし、バックアップから必要なファイルを回復させる。わけの分からないVMPlayerのイメージファイルと格闘するよりずっと手っ取り早いという判断。もともとこのFC14はFC14をターゲットにしたプロジェクトのビルドだけを目的として、その他の汎用性はまったく求めていないのでカスタマイズした部分もわずかだ。

エディタやバックアップなどのファイル管理は、すべてセントラルサーバであるFC13に任せ、FC14はプロジェクトの開発ディレクトリのみをNFSマウントして、ビルド作業だけを実行する。以前は/home をまるごとマウントしていたが、ホームディレクトリのしたの種々の設定ファイルがぶつかるとあまりおもしろくないので、プロジェクトの開発ディレクトリだけをマウントすることにした。

ところが通常どおり/etc/fstab にNFSのマウントを記述するとマウントはするが、マウントポイント以下のファイルの所有者がすべて nobody:nobody になってしまう。 このマウントされるサーバのディレクトリは他のマシンにもマウントされているが、そちらは正常なファイルの所有者になっている。このままでは用をなさない。

条件をまとめてみる。
  • FC13がNFSサーバ
    /home/h/project をエfc14にクスポート(rw,wdelay,no_root_squash,no_subtree_check)
  • FC14がNFSクライアント
    mount -t nfs4 fc13:/home/h/projects /home/h/projects
  • FC13とFC14のユーザ「h」は同じID(501)
  • マウントは成功するが、FC14上でマウントしたディレクトリを見るとファイル所有者が nobody:nobody になっている
調査の結果、こちらからの情報が役に立った。

クライアント(FC14)の /etc/idmapd.conf を編集して


#Domain = localdomain.edu

の部分を

Domain = mydomain.edu

のように本物のドメイン(でなければならないのかどうかは不明)に書き換え、

# service rpcidmapd restart

を実行し、マウントをし直す。これでマウントされたディレクトリとその下のファイルが正しいファイル所有者になった。

今回はマウントするファイルの所有者がただ一人だったから、FC14側の ユーザIDをFC13側のIDと一致するように手で書き換えたが、複数のユーザIDが存在するならNISを使うのがよいと思う。

2014年6月17日

Fedora 20: VNC で MATE を走らせる

我が家のサーバ群は4年ほど前に構築した32ビットのFedora 13(FC13)ベース。さすがにいろいろなところが古くなってきたので、最新のFedroraに更新を計画しているが、実際の更新に先立って、まず現在のところ最新のFC20でテスト用サーバを1台作っている。

この戦略は正しかった。FC13とFC20ではあまりにもいろいろなところが違いすぎる。これから違いを一つ一つ確認していくが、取りあえず多くの違いの元凶一つとしてとして見つかったのは、SystemVのスタートアップスクリプトからsystemdへの変更。

今回はVNCサーバをやっつける。

小生はFC15から標準デスクトップになったGNOME3は生理的に合わない。小生のデスクトップの使用はXTerminalでCUIの作業が主体だから、ああ言うWindows8に迎合したようなデスクトップは使えない。実を言うと、GNOME2に固執しているのがFC13から4年間も更新を躊躇していた原因。しかし最近のリリースではGNOME2の後継のMATEが選べるようになったので漸く腰をあげた。

で、ローカルのデスクトップは指示どおりにインストールして何事もなく動作し、GNOME2風のデスクトップで安心したのだが、VNCで躓いた。

systemd以前のVNCサーバは /etc/sysconfig/vncserver に起動したいVNCセッションを書き込んでサービスを起動すればよかったが、systemd になって手順が変わった。以下が大まかなフロー。
  1. /usr/lib/systemd/system/vnserver@.service/etc/systemd/system/vncserver@:n.service にコピー(nは1~の任意の数字。5900+n がサーバのポート番号になる)
  2. /etc/systemd/system/vncserver@:n.service を編集。ほとんどの場合、<USER>を任意のユーザ名に変更するだけ。画面解像度の指定は、/usr/bin/vncserver のコマンドラインに以下のように)。

    ##ExecStart=/sbin/runuser -l <USER> -c "/usr/bin/vncserver %i"
    ExecStart=/sbin/runuser -l vncuser -c "/usr/bin/vncserver -geometry 1280x720 %i"
  3. vncpasswd を実行して ~/.vnc ディレクトリとパスワードファイルを作成。
  4. ~/.vnc/xstart を編集して望みのデスクトップ(ウィンドウマネージャ)を指定。
上記の1~3はいろいろなところで解説されているが、4.のデスクトップの指定は試行錯誤の結果。

MATEを指定するには、簡単に言えば、~/.vnc/xstart の内容を全部無視して、以下の一行を実行させるようにすればよい。
exec mate-session &
 具体的には、~/.vnc/xstart の内容を全部削除あるいはコメントアウトして上記の行をいれるか、または上記の行をファイルの先頭に挿入するだけ。

簡単なことほどどこにも書かれていないので見つけるのに苦労する。

2014年6月14日

BTRFS の RAID 機能

BTRFSはRAID機能を内蔵している。

LinuxのRAIDはmdの使用が一般的だが、今回は以前報告した「BTRFSのFIEMAP/filefragは嘘をつく」と併せてこの内部RAID機能を検証する。

検証に使った 3.0.101カーネルは以下のRAIDレベルをサポートする。
  • JBOD - リニアな複数ディスクの結合。冗長性なし
  • RAID0 - ストライピングによるアクセス性能向上。冗長性なし
  • RAID1 - ミラーリング。100%冗長
  • RAID10 - RAID0 を RAID1上で動作。ストライピングによるアクセス性能向上と100%の冗長性
上記より新しいカーネルではRAID5(水平パリティ)とRAID6(二重水平パリティ)も実現されているが、BTRFSのWikiにはまだ開発中でバグが多いので使用するな、と警告がある。

まず、実験のためにloopデバイスを二つ作り、JBODをマウントする。

root@h-rn312:~# truncate -s 100M /data/img/loop-img.0 /data/img/loop-img.1
root@h-rn312:~# losetup /dev/loop0 /data/img/loop-img.0
root@h-rn312:~# losetup /dev/loop1 /data/img/loop-img.1
root@h-rn312:~# mkfs.btrfs -f /dev/loop0 /dev/loop1 
SMALL VOLUME: forcing mixed metadata/data groups
WARNING! - Btrfs v3.14.1 IS EXPERIMENTAL

WARNING! - see http://btrfs.wiki.kernel.org before using

Performing full device TRIM (100.00MiB) ...

Turning ON incompat feature 'mixed-bg': mixed data and metadata block groups
Created a data/metadata chunk of size 8388608
Performing full device TRIM (100.00MiB) ...
adding device /dev/loop1 id 2
fs created label (null) on /dev/loop0
    nodesize 4096 leafsize 4096 sectorsize 4096 size 200.00MiB
Btrfs v3.14.1
root@h-rn312:~# mount -o rw /dev/loop0 /mnt
mount: block device /dev/loop0 is write-protected, mounting read-only
root@h-rn312:~# mount -o remount,rw /dev/loop0 /mnt


mkfs時に二つのディスクデバイスを組み合わせる(デフォルトはJBOD)ことを宣言しているので、マウントはどちらのデバイス名でもよい。後から「btrfs device add /dev/loop2 /mnt」のように追加もできる。一回目のマウントが読み出しオンリーになってしまったが理由は不明。

複数ディスクを使ってBTRFSのRAIDを構築・構成する方法の詳細はBTRFSのWikiを参照してください。

これに50MBのダミーのファイルを作る。

root@h-rn312:~# dd if=/dev/urandom of=/mnt/jbod bs=1M count=50

このファイルをfilefragでエクステントの状況を見る。使ったのは最新のe2fsprogs-1.42.10からビルドしたfilefrag。以前のバージョンより冗長な出力を見せる。

root@h-rn312:~# filefrag -v /mnt/jbod
Filesystem type is: 9123683e
File size of /mnt/jbod is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..    3199:       3072..      6271:   3200:          
   1:     3200..    7999:       8192..     12991:   4800:       6272:
   2:     8000..   12799:      13312..     18111:   4800:      12992: eof
/mnt/jbod: 3 extents found

しかし、ここで表示されるFIEMAPioctl出力の「物理オフセット」は「BTRFSのFIEMAP/filefragは嘘をつく」で報告したようにBTRFSが内部で使う「論理オフセット」(filefragのファイル「論理オフセット」と混同しないこと)で、実際の物理ディスクのオフセット(位置)とは関係ない。

そこで特別にパッチを当てたカーネルとfilefragを使って本当の物理ディスクオフセットを見てみる。
root@h-rn312:~# filefrag -v -P /mnt/jbod
Filesystem type is: 9123683e
File size of /mnt/jbod is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length: device: flags:
   0:        0..    3199:        256..      3455:   3200:   7:1
   1:     3200..    7999:       3072..      7871:   4800:   7:0
   2:     8000..   12799:       5376..     10175:   4800:   7:1   eof
/mnt/jbod: 3 extents found

device」フィールドは実際に使用されている物理ディスク(この場合はloopデバイス)のメジャー:マイナー・デバイス番号。これを見ると、ファイルはloop0デバイスとloop1デバイスにストライプ的に分散されており、普通「JBOD」でイメージする単純なリニア結合とは違い、むしろ複数ディスクからなるストレージプールにパフォーマンスを考慮した分散と解釈するのが適当のようだ。

続いて、同じようにして3台のディスクからなるRAID0(ストライピング)を試してみる(ここではloopデバイスでなく/dev/sdb*を使ったのでデバイス番号がJBODの例と違うが、うまく脳内変換してください)。

root@h-rn312:~# filefrag -v /mnt3/raid0
Filesystem type is: 9123683e
File size of /mnt3/raid0 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..   12799:       7158..     19957:  12800:             eof
/mnt3/raid0: 1 extent found
root@h-rn312:~# filefrag -v -P /mnt3/raid0
Filesystem type is: 9123683e
File size of /mnt3/raid0 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length: device: flags:
   0:        0..   12799:       1622..      5887:   4266:   8:21  eof
   0:        0..   12799:       1616..      5887:   4272:   8:20  eof
   0:        0..   12799:       4432..      8693:   4262:   8:19  eof
/mnt3/raid0: 1 extent found
root@h-rn312:~# filefrag -v -P -X /mnt3/raid0
Filesystem type is: 9123683e
File size of /mnt3/raid0 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length: device: flags:
   0:        0..    31ff:        656..      16ff:   10aa:   8:21  eof
   0:        0..    31ff:        650..      16ff:   10b0:   8:20  eof
   0:        0..    31ff:       1150..      21f5:   10a6:   8:19  eof
/mnt3/raid0: 1 extent found

物理エクステントの出力を見ると、/mnt/raid0ファイルの1論理エクステント(0~12799)に3物理エクステントが/dev/sdb[1-3](8:19~8:21)に分散して対応しており、filefragの出力からは分からないが各ストライプの長さは64KB(16ページブロック)。物理エクステントは連続してファイル内容を保持してはおらず、ストライプの続きは次のディスクのストライプに連続して、ディスクの使用は論理エクステントの終端まで循環する。

eof」のフラグは filefragが単に最終論理エクステントに表示するだけなのであまり意味はない

-Xオプションをつけた16進表示も見ると、物理エクステントの開始オフセットは3台のディスクで揃っていないどころか、ストライプの境界にも揃っておらず、さらには長さもバラバラなことが分かり、mdのディスク・アドレス空間で一様なストライピングとは様相が違うことが分かる。

次は2台のディスクを使ったRAID1(ミラーリング)。

root@h-rn312:~# filefrag -v /mnt2/raid1
Filesystem type is: 9123683e
File size of /mnt2/raid1 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..    6399:       5129..     11528:   6400:          
   1:     6400..    9599:      11529..     14728:   3200:          
   2:     9600..   12799:      21504..     24703:   3200:      14729: eof
/mnt2/raid1: 2 extents found
root@h-rn312:~# filefrag -v -P /mnt2/raid1
Filesystem type is: 9123683e
File size of /mnt2/raid1 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length: device: flags:
   0:        0..    6399:       2313..      8712:   6400:   8:18
   0:        0..    6399:       5129..     11528:   6400:   8:17
   1:     6400..    9599:       8713..     11912:   3200:   8:18
   1:     6400..    9599:      11529..     14728:   3200:   8:17
   2:     9600..   12799:      18688..     21887:   3200:   8:18  eof
   2:     9600..   12799:      21504..     24703:   3200:   8:17  eof
/mnt2/raid1: 3 extents found

三つの論理エクステントのそれぞれが 2台のディスクの同じサイズの物理エクステントで構成されているのはよいとしても、ペアになる物理エクステントの開始オフセットは揃っていない。どうもmdのような完全ディスクレベルのミラーリングとは違い、ファイルエクステント単位のRAIDを実現していることがますますはっきりしてきた。

最後はRAID10。最低4台のディスクが必要(mdでは3台でもRAID10を実現できる)。

root@h-rn312:~# filefrag -v /mnt4/raid10
Filesystem type is: 9123683e
File size of /mnt4/raid10 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..   12799:       7174..     19973:  12800:             eof
/mnt4/raid10: 1 extent found
root@h-rn312:~# filefrag -v -P /mnt4/raid10
Filesystem type is: 9123683e
File size of /mnt4/raid10 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length: device: flags:
   0:        0..   12799:       2310..      8709:   6400:   8:25  eof
   0:        0..   12799:       2310..      8709:   6400:   8:24  eof
   0:        0..   12799:       2304..      8703:   6400:   8:23  eof
   0:        0..   12799:       5120..     11519:   6400:   8:22  eof
/mnt4/raid10: 1 extent found

RAID1と同様、物理オフセットは揃っていない。

さらに動作を検証すると、RAID機能はリアルタイムではなく、一旦普通のシングルディスクファイルで展開されたエクステントをバックグラウンドでRAIDに再構築しているようだ。

下は、RAID0にファイルを作成した直後と10秒ほど経ってからの物理エクステントの様子。最初は/dev/sdb1だけで作られたファイルが/dev/sdb[1-3]に分散される様子が見える。

root@h-rn312:~# ./filefrag -v -P /mnt3/raid0
Filesystem type is: 9123683e
File size of /mnt3/raid0 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length: device: flags:
   0:        0..    1023:          0..      1023:   1024:   8:19
   1:     1024..    2047:          0..      1023:   1024:   8:19
   2:     2048..    3071:          0..      1023:   1024:   8:19
   3:     3072..    4095:          0..      1023:   1024:   8:19
   4:     4096..    5119:          0..      1023:   1024:   8:19
   5:     5120..    6143:          0..      1023:   1024:   8:19
   6:     6144..    7167:          0..      1023:   1024:   8:19
   7:     7168..    8191:          0..      1023:   1024:   8:19
   8:     8192..    9215:          0..      1023:   1024:   8:19
   9:     9216..   10239:          0..      1023:   1024:   8:19
  10:    10240..   11263:          0..      1023:   1024:   8:19
  11:    11264..   12287:          0..      1023:   1024:   8:19
  12:    12288..   12799:          0..       511:    512:   8:19  eof
/mnt3/raid0: 13 extents found
root@h-rn312:~# ./filefrag -v -P /mnt3/raid0
Filesystem type is: 9123683e
File size of /mnt3/raid0 is 52428800 (12800 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length: device: flags:
   0:        0..   12799:       1622..      5887:   4266:   8:21  eof
   0:        0..   12799:       1616..      5887:   4272:   8:20  eof
   0:        0..   12799:       4432..      8693:   4262:   8:19  eof
/mnt3/raid0: 1 extent found

と言うことで、BTRFSのRAID機能は、その実現方法がファイルシステムに依存しているらしいことが分かった(当然か)。LVMなしでJBODが簡単に実現できることはメリットがあるが、「本物の」RAID機能はBTRFS内部で一体化しておりレイヤの切り分けができないので、システムの拡張とか事故が起きたときの 対応はどうだろうかという疑問が残る。得てしてこう言う「一体型」ソリューションは、そのシステムが想定しているモデルから少しでも外れると途端に使いにくくなるもの、と言う経験則からは躊躇する。

2014年5月28日

アトミック・カーネルスレッドと言うものがあるらしい

BTRFSのディスクからの読み込み終了時の関数 btrfs_readpage_end_io_hook() をいじっている。

目的とした機能が動作して最終テストを行っていたら、dmesg が下のように文句を言っている。

BUG: scheduling while atomic: btrfs-endio-1/1157/0x00000001
Modules linked in:
Pid: 1157, comm: btrfs-endio-1 Not tainted 3.0.101.RNx86_64.2.2+ #121
Call Trace:
 [<ffffffff8887d4bd>] ? __schedule_bug+0x54/0x58
 [<ffffffff8888712b>] ? __schedule+0x59b/0xa30
 [<ffffffff88398299>] ? put_dec+0x59/0x60
 [<ffffffff880d0be4>] ? add_partial+0x24/0x80
 [<ffffffff880d240e>] ? deactivate_slab+0x4e/0xf0
 [<ffffffff88774849>] ? __alloc_skb+0x99/0x270
 [<ffffffff8888768a>] ? schedule+0x3a/0x50
 [<ffffffff88042e6d>] ? sys_sched_yield+0x3d/0x50
 [<ffffffff888878bf>] ? yield+0x2f/0x40
 [<ffffffff88799ea0>] ? netlink_broadcast_filtered+0x330/0x480
 [<ffffffff8879a009>] ? netlink_broadcast+0x19/0x20
 [<ffffffff883ad39e>] ? nla_put+0x2e/0x40
 [<ffffffff883488af>] ? mdcsrepair_netlink+0x46f/0x580
 [<ffffffff882e6711>] ? btrfs_readpage_end_io_hook+0x1d1/0x3a0
 [<ffffffff882fd64e>] ? end_bio_extent_readpage+0x12e/0x9c0
 [<ffffffff88106fe8>] ? bio_endio+0x18/0x30
 [<ffffffff882d864f>] ? end_workqueue_fn+0x3f/0x50
 [<ffffffff8830d3f0>] ? worker_loop+0x140/0x500
 [<ffffffff8830d2b0>] ? btrfs_queue_worker+0x300/0x300
 [<ffffffff88063a09>] ? kthread+0x89/0x90
 [<ffffffff8888aa74>] ? kernel_thread_helper+0x4/0x10
 [<ffffffff88063980>] ? kthread_worker_fn+0x140/0x140
 [<ffffffff8888aa70>] ? gs_change+0xb/0xb


調べてみると、in_atomic_preempt_off() が真になっていることが直接の原因。どうもこのワーカースレッドはプリエンプションだけでなくスケジューリングも禁止しており、スレッドとは言え、割り込み処理のようにCPUを手放さずにスレッド終了まで実行しなければならないようだ。このコールスタックでは nla_put()(Netlinkのメッセージ構築関数)がスケジュールしてしまうためにバグとしてスタックダンプをしている。実際にはスタックダンプがあっても目的の機能は実行されて問題がないのだが、そこはホレ、やはりこういうものは出現させるべきではない。

解決策としては、 nla_put()をやめる訳には行かず、またこのスレッドをノン・アトミックにするのもはばかれるので、一計を案じてアトミックでない通常のスレッドを別に走らせてそこに必要なデータを渡してnla_put()以降の処理を依頼することにした。ここでは無限ループのサービススレッドでうまく行ったが、1回限りのワーカースレッドでもうまく行くかどうかは試していない。

2014年5月13日

古いラップトップの延命策 - 802.11n と HDDからSSDへの換装

小生が家でメインに使っているPCはDELLのXPS M1330というラップトップ。7年前に、当時働いていた会社がDELLに買収されたときに個人用として各従業員がタダで貰った。Core 2 Duo T7500 2.2GHz の CPU でメモリは 4GB、160GB の HDD。

Windows Vista(Business、32bit)と言うWindows8ほどではないものの最悪に近いOSであることと画面が1280x800と少々小さめであることを除けば、最近のMacほどはないものの、ウェッジ型の薄く見える形状で気に入っている。毎年のバケーションにもいろいろな国に持って行っており、バッテリーは3代目だ。

気に入っているとは言え、このラップトップは、1年以上使用されているすべてのPCに共通する問題を抱えている。

お・そ・い
今年は家族メンバーのPCのアップグレードが2件予定されており(1件は実施済み)、新しいラップトップに買い換える予算がない。

そこで、最小限の費用での延命策を検討した結果、採用したのが以下の2項目。
  • WiFi の 802.11g から 802.11n への変更
  • HDDをSSDに換装
WiFi の変更は、手元に小型の802.11n 小型USBドングルがあったので試しに入れてみた。あまり期待はしていなかったのだが、実際に体感できる速度の向上(30~40%?)があったのは儲けもの。単価$10前後で実施できる。本当は内臓の WiFi が交換できれば、USB端子も消費せずに済むのだが、ないものねだりをしても仕方がない。

ドングルを挿して、付属のドライバをインストールする。コントロールパネルから内臓の WiFi を禁止してお終い。あ、MACアドレスで固定のDHCPを行っている場合は、当然DHCPサーバのMACアドレスを変更しなければならない。

ただし、ドングルにもいろいろあるようで、最初はRaspberry Pi を買った時に付属してきた(Linuxでサポートされていない、したがって役に立たない)Realtek RTL8188CU を使っていたが、どうも不安定で、20分ごとにデバイスが再起動していた。Raspberry Piで実績のあったEdimaxに変更したら随分安定になった。

インターネットに使用しているケーブルモデムは、最近スピードサイトで測ったら60Mbps程度。家の中のLANも100Mbpsから1Gbpsに順次交換している。WiFiも速くすると効果的と判った。

HDDからSSDへの換装は、依然SSDで否定的な経験をしたので躊躇していたが、もうそろそろ時期と思い、再び実践。結果は
は・や・い
の一言。まるで購入時のあのサクサクさが戻ってきたようだ。

使用したSSDは SanDisk の 256GB。WDの普及版1TB HDD と比べるとバイトあたり単価は約8倍。
  • SSD 256GB $120
  • HDD  1TB $60
将来はこの価格差が縮む可能性はあるか?

SSDの容量は内部チップの面積(数)に正比例し、これが価格の大部分を占めるが、HDDでは、プラッタを除く、ハウジングやアクチュエータ、ボイスコイル、PC基盤といった主要部品は容量にあまり依存しないので、容量と価格は正比例しないと思う。だから、単純な価格比にはならないだろう。

HDDからSSDへの換装は、マザーボードに空きSATAコネクタが2個あるLinuxを使うと至極簡単。

まず、HDDより少し大きめの容量のSSDを買って来る。我が家の場合は160GB -> 256GBとして、SanDisk のUltra Plus にした。

データのバックアップは言われなくてもちゃんと取ること。小生はGenie9というWindowsのバックグラウンド型バックアッププログラムを使っているので、新しいファイルシステムに変更があると即座にその分がバックアップされるようになっている。

ラップトップをシャットダウン(「スリープ」はだめ)して、バッテリを取り去り、HDDを外す。Linux マシンにHDDとSSDを装着して起動。以下のコマンドを実行して数十分待てばディスクの複製が完了(HDDが/dev/sdb、SSDが/dev/sdcの場合)。
[root@linux ~]# dd if=/dev/sdb of=/dev/sdc bs=16M

HDDのあったところにSSDを装着して蓋を閉め、バッテリを戻してラップトップを起動すると、もしかしたらBIOSが何か言ってくるかもしれないが、すべて肯定的に返事をすれば、きっと立ち上がり時間が1/10になっていることに驚くはず。

元のHDDより大容量のSDDを使ったので、ディスクの後ろの方が余る。小生が実践した換装では、86GBほど余ってしまった。WindowsでCドライブしか使っていなければ、新たなパーティションを作ってDドライブとして使うのが一番簡単。小生の場合はちょっと複雑で、Cの後ろにバックアップの対象としないVMWareの仮想ドライブファイルを格納するVドライブが既にある(バックアップはVMWareのゲストOS内で実行)。新たなパーティションでやたらドライブ数を増やしても仕方がないので、この新しいパーティションはVドライブをコピーしてきて、以前のVドライブの場所はCドライブを延長しようかと考えている。ま、この辺は各自でどうぞ。

これで7年前のラップトップが快調に甦った。あと3年ぐらいは使えそうな感じがする。

大抵の突然のわけの分からない不具合はSELINUXを禁止すると治る

今までに何回わけの分からない不具合がこれだったか…。

ほとんどの場合は、POSIXのファイルパーミッションでは不可思議な現象に関連していたが、今回はFedora 20で新しいサーバを構築中に起こった。

下は yum  実行時に起きたわけの分からないエラーメッセージ。リポジトリサーバがダウンしていたのかと思い半日待ったが埒があかない。「まさか」と思いつつ /etc/selinux/config で「SELINUX=disabled」にしたらあっけなく治った。

[root@mill3 ~]# yum makecache
Loaded plugins: langpacks, refresh-packagekit


 One of the configured repositories failed (Unknown),
 and yum doesn't have enough cached data to continue. At this point the only
 safe thing yum can do is fail. There are a few ways to work "fix" this:

     1. Contact the upstream for the repository and get them to fix the problem.

     2. Reconfigure the baseurl/etc. for the repository, to point to a working
        upstream. This is most often useful if you are using a newer
        distribution release than is supported by the repository (and the
        packages for the previous distribution release still work).

     3. Disable the repository, so yum won't use it by default. Yum will then
        just ignore the repository until you permanently enable it again or use
        --enablerepo for temporary usage:

            yum-config-manager --disable <repoid>

     4. Configure the failing repository to be skipped, if it is unavailable.
        Note that yum will try to contact the repo. when it runs most commands,
        so will have to try and fail each time (and thus. yum will be be much
        slower). If it is a very temporary problem though, this is often a nice
        compromise:

            yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot retrieve metalink for repository: fedora/20/x86_64. Please verify its path and try again

SELINUX は本当に質(たち)が悪い。

2014年5月10日

Fedora 20: em1 ではなく eth0 にしたい

我が家のサーバ群は4年前に構築した Fedora 13(以下FC13)。最近のOpenSSLのセキュリティホールなどの記事を読むに連れ、そろそろ更新の時期と考え、まずはセキュリティに一番関係するファイアウォールのアップグレードから始めた。32ビットから64ビットへの移行も大きな動機。
 
選んだのは現在最新のFC20。世の中はUbuntu系が急速に普及しているが、Ubuntu系は経験的にワークステーションにはよくても、systemd採用以前のバージョンはいろいろなサービスの直交性が低い印象があり、また今まで延々とFedoraで作ってきたいろいろな自家製サービスやツールを考えると、ここ暫くはFedora系に固執するのが安全という判断。

幸いなことに、あのWindows8もどきの最悪なデスクトップ(Gnome3)を嫌う多くの人に支持されたGnome2の直径子孫のMATEのスピンがあるのでそれを使うことにした。

前置きが長くなったが、取りあえず古いPC上に実験的にMATEをインストールする。インストーラのanacondaが使いにくくなったなど色々あるが、それらは別稿で書くとして、一番驚いたのは、ネットワーク・デバイス名が「ethX」から「emY」に変更されていること。ifconfigの出力フォーマットの変更(こちらの方が影響の可能性が大きそう)もさることながら、実害の可能性は低いと思うが、もしかしたら自家製のツールのもうとっくに忘れた部分でわけの分からない壊れ方をするかもしれない。

[root@mill3 ~]# ifconfig
em1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::6670:2ff:fe11:29eb  prefixlen 64  scopeid 0x20<link>
        ether 64:70:02:11:29:eb  txqueuelen 1000  (Ethernet)
        RX packets 139  bytes 21434 (20.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 648 (648.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ぐぐってみると、どうもこの仕様はFC15あたりから導入されたらしいが、「emY から ethX に戻したい」と言う同じ悩みを抱えた人は大勢いるらしく、結構な数の記事がヒットする。それらを色々試してみるがうまく行かない。結局たどり着いたのはこちらの記事

これによると、以下の2ヶ所を手当てすればよい。
  • grub の設定ファイルのカーネルへの引数
  • /etc/sysconfig/network-scripts/ifcfg-eth* の作成
  • udev のルールファイルを /etc/udev/rules.d/60-net.rules でオーバライド
他の様々な記事では、udev のルールファイルを変更するとよいと書いてあるが、ルールファイルを変更(と言うよりオーバライド)しただけでは何も変化はなかった。

まず、カーネルのブート時にemYへのリネームを禁止する引数を渡す。

/boot/grub/grub.conf と言うgrub独自言語のファイルが /boot/grub2/grub.cfg と言うシェルスクリプトに変わってしまったことにも驚いたが、とにかく集めた情報を基に、以下のパラメタ(net.ifnames=0 biosdevname=0)をカーネルの起動時コマンドラインに追加する。

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Fedora (3.14.2-200.fc20.x86_64) 20 (Heisenbug)' --class fedora --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-3.11.10-301.fc20.x86_64-advanced-4964450e-bb98-4b4e-ac6f-cfd3c0f272c6' {
        load_video
        set gfxpayload=keep
        insmod gzio
        insmod part_gpt
        insmod part_gpt
        insmod diskfilter
        insmod mdraid1x
        insmod ext2
        set root='mduuid/2978f1951a5f27f2fa616dc44e7225c6'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint='mduuid/2978f1951a5f27f2fa616dc44e7225c6'  4dd4ee0a-f0ce-48a1-894d-e329ee6ac3da
        else
          search --no-floppy --fs-uuid --set=root 4dd4ee0a-f0ce-48a1-894d-e329ee6ac3da
        fi
        linux   /vmlinuz-3.14.2-200.fc20.x86_64 root=UUID=4964450e-bb98-4b4e-ac6f-cfd3c0f272c6 ro rd.md.uuid=2978f195:1a5f27f2:fa616dc4:4e7225c6 rd.md.uuid=f52a49f7:ae3bca96:5f1530ab:cd3bd27c rd.md.uuid=d085c9c0:4d36caf6:ec19a007:ad1e1c74 vconsole.font=latarcyrheb-sun16  rhgb quiet LANG=en_US.UTF-8 net.ifnames=0 biosdevname=0
        initrd /initramfs-3.14.2-200.fc20.x86_64.img
}
以下略


上記記事によれば、FC18までは「biosdevname=0」だけでよかったが、FC19以降ではこれだけだと以下のようなドライバの付けたデバイス名になってしまう。

[root@mill3 ~]# ifconfig
enp1s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::6670:2ff:fe11:29eb  prefixlen 64  scopeid 0x20<link>
        ether 64:70:02:11:29:eb  txqueuelen 1000  (Ethernet)
        RX packets 139  bytes 21434 (20.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 648 (648.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

/boot/grub2/grub.cfg は新しいカーネルがインストールされる度に書き換えられてしまうので、この変更を将来も自動的に行うためには、/etc/default/grub にこれらを追加しておく。

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.md.uuid=2978f195:1a5f27f2:fa616dc4:4e7225c6 rd.md.uuid=f52a49f7:ae3bca96:5f1530ab:cd3bd27c rd.md.uuid=d085c9c0:4d36caf6:ec19a007:ad1e1c74 vconsole.font=latarcyrheb-sun16 $([ -x /usr/sbin/rhcrashkernel-param ] && /usr/sbin/rhcrashkernel-param || :) rhgb quiet
net.ifnames=0 biosdevname=0"
GRUB_DISABLE_RECOVERY="true"

または、/etc/default/grub を変更しておいて「grub2-mkconfig -o /boot/grub2/grub.cfg」を実行しても同じこと。

Fedora のインストール時には /etc/sysconfig/network-scripts/ifcfg-em* と言うシェルスクリプトのインクルードファイルが各々のネットワークインタフェース用に作成される。これがないと ifup とか ifdown が動かない。ifcfg-em* を ifcfg-eth* にリネームして、内容の「NAME=emY」を「NAME=ethX」に変更する。

後はリブートしてやれば、下のように目出度く馴染みの「ethX」 になっているはず。

[root@mill3 ~]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::6670:2ff:fe11:29eb  prefixlen 64  scopeid 0x20<link>
        ether 64:70:02:11:29:eb  txqueuelen 1000  (Ethernet)
        RX packets 20  bytes 3596 (3.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8  bytes 648 (648.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ネットワークインタフェースが一つだけならこれでお終いでもよいが、我が家のファイアウォール・ゲートウェイのように複数のインタフェースがある場合は、 それぞれのインタフェースに決定的な名前を付けたい。そのためには udev のルールファイルを使う。

最近のLinuxのディストリビューションでは、/usr/lib にディストリビューションのデフォルト設定ファイルを置き、ローカルの設定変更は /etcの下の同名のファイルでオーバライドするようになっている。Fedora 20 のネットワークインタフェースの udev の場合は、既にデフォルトファイルとして /usr/lib/udev/rules.d/60-net.rules があるので、以下のような /etc/udev/rules.d/60-net.rules を作ってオーバライドする。

[root@mill3 ~]# cat /etc/udev/rules.d/60-net.rules
# PCI device 0x1011:0x0019 (tulip)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:c0:f0:4c:f5:78", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

# PCI device 0x10ec:0x8168 (r8169)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="60:a4:4c:b5:26:48", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"

と言うのは、udev のマニュアルページに書かれていることだが、 実際は違うようだ。この方法を実践してみたが、どうも eth0eth1 の割り付けが不安定で、ブートの度にひっくり返ることがある。そこで、/etc/udev/rules.d の方を 61-net.rules に改名したら安定したように見える。マニュアルページの記載とは異なり、/etc/usr/lib 間の優先度の違いはないように見える。優先したい方(/etc 側)を文字列的に後になるような名前にした方が無難のようだ。

一部のサーバ群の管理では「emY」の方が論理的・仮想的で便利だという意見もあり、それがこの「ethX」から「emY」への変更の理由なのだろうが、過去との互換性の面では困った「新しもの」だ。エイリアスを許せるようにだとかできないのだろうか?

2014年5月8日

BTRFSのFIEMAP/filefragは嘘をつく

FIEMAPioctlとそれを使ったfilefragコマンドは、ディスク上に散らばったファイルの実体(エクステント)を返す・表示するので、ファイルの断片化状態を見たりするのに使う。ところが、BTRFSのFIEMAPで返されるエクステントのディスク上のセクタ番号(=アドレス)は実際の物理ディスク上の位置と一致しない。

これは、BTRFSが内部にRAID機能を持っており、FIEMAPで返されるエクステントの位置情報は、この内部RAID上の位置だからだ。

3.0カーネルに含まれるBTRFSは
  • ストライピング(RAID0)
  • ミラリング(RAID1)
  • ストライピング+ミラリング(RAID10)
  • 複数ディスクの連結(JBOD)
をサポートしており、mkfs.btrfs でファイルシステム構築時にRAIDに使用するディスクを指定、または稼働中に btrfs device add/delete で使用するディスクを増減できる。

この内部RAID上のエクステント情報(FIEMAPが返すのと同じ)は内部的には「論理エクステント」、実際のディスク上のエクステント情報は「物理エクステント」と呼ばれており、外部から利用できる明確なレイヤの境界はなく、したがって論理エクステントと物理エクステントを変換するAPIもないようだ。

この論理エクステントと物理エクステントの変換に関わるコードは fs/btrfs/volumes.c にほとんどが含まれている。

さらにややこしいのは、 BTRFSのファイルをstatして得られるデバイス番号は、本物のメジャー:マイナー番号ではなく、BTRFSが内部的に作成するサブボリュームデバイスの仮想的なデバイス番号だと言うこと。

例えば、下の例では /mnt にマウントされた /dev/md91と言う md のRAID上のファイル /mnt/etc/ld.so.cache を見ているが、/dev/md919:91 のデバイス番号にも関わらず、ファイルを stat すると 0:35 が返される。これはカーネル内部では無名(anonymous)デバイス番号と呼ばれ、メジャー番号は常にゼロ、マイナー番号はデバイス登録の要求がある度に1づつ増える(登録取り消しデバイス番号の再利用はない)。この無名デバイス番号と物理デバイスの関係を解決する直接的な手段はない。

root@h-rn312:~# df -h /mnt/etc/ld.so.cache
Filesystem      Size  Used Avail Use% Mounted on
/dev/md91       396M  2.5M  390M   1% /mnt
root@h-rn312:~# ls -l /dev/md91
brw-rw---T 1 root disk 9, 91 May  7 14:37 /dev/md91
root@h-rn312:~# stat /mnt/etc/ld.so.cache
  File: `/mnt/etc/ld.so.cache'
  Size: 30475         Blocks: 64         IO Block: 4096   regular file
Device: 23h/35d    Inode: 1164        Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2014-05-07 12:50:28.426131463 -0700
Modify: 2014-05-07 12:50:28.426131463 -0700
Change: 2014-05-07 12:50:28.426131463 -0700
 Birth: -

root@h-rn312:~#

現在、FIEMAPを拡張してファイルの物理エクステント情報と物理デバイス番号を返せる機能を開発中。

なお、ファイルサイズが小さい場合(上限サイズは不明)は、ファイル本体がメタデータと一緒に格納されるようで、その場合はfilefragはディスクポジションとしてゼロを表示する。

注意:上で「ディスク」「物理デバイス」と書いたが、これはあくまでもBTRFSの下位のレイヤと言う意味で、実際に物理ディスクまたはそのパーティションでなくても、mdのRAIDだとか、LVMのボリュームだとか、loopデバイスだとか、要するにブロックデバイスなら何でもよい。

2014年5月7日

__func__ はマクロではない

GCCに備わっている__func__は、C99規格に適合して、そのスコープの関数名の文字列にコンパイラが自動変換するので、デバッグ用に便利だ。しかし、この文字列への変換はC言語で言うところの「マクロ」ではない。

だから、以下のような使用はコンパイルエラーになる。


size_t some_func(void)
{
        return strlen("<" __func__ ">");
}


むしろ、以下の宣言と等価と考えた方がよい。

size_t some_func(void)
{
        static const char __func__[] = "some_func";
        return strlen("<" __func__ ">");
}

これはGCCのマニュアルに明記されている。

__FILE__とか__LINE__は本物のマクロだから、strlen("<" __FILE__ ">")のような書き方ができる。

ただし、もう一つの注意点は、__FILE__はプリプロセッサがファイルを処理したときのファイルパスだから、#includeで展開されたファイルパスの__FILE__は、例えば /usr/include/myheader.h になるし、Linuxのカーネルのソースファイル中では fs/btrfs/inode.c のようになる。

2014年5月2日

マウントされたファイルはマウント元をアップデートすべし

Fedroraでは、セキュリティ向上のため、named(BIND)は/var/named/chroot/というディレクトリにchrootして実行するようになっている。ところがこのchrootはデフォルトオプションで、普通のルートでも実行できる。/var/named/chroot/の下には、namedの実行ようの最小限のファイルツリーが必要。これらのファイルは当然「本物の」ルートの下のそれと重複するが、これらは別個のファイルではなく、namedのスタートアップスクリプトが(ほとんどはディレクトリごと)/var/named/chroot/の下のサブディレクトリにBINDのマウントをしている。ハードリンクを作るより明示的で間違いない。

例えば、/var/named//var/named/chroot/var/named/にマウントされるので、namedが/var/named/chroot/にchrootしてもしなくても同一の/var/named/が見える。

ところで我が家のメインDNSサーバは、スパムメイル受信低減とセキュリティ対策のためにdnsblというスクリプトをcronで定期的に起動し、外部のIPアドレスブラックリストを走査し、/var/named/named.dnsblというゾーンファイルを作成してnamedに再ロードさせている。ところが、どういうわけか、このスクリプトがゾーンファイルを/var/named/chroot/var/named/named.dnsblに作成するようになっていた。

定常状態では/var/named/named.dnsblでも/var/named/chroot/var/named/named.dnsblでも同じことだが、システムの起動と終了時にはレイス状態が発生し、/var/named/chroot/var/named/named.dnsblが/var/named/named.dnsblのマウントではなく本物のファイルが作成されてしまうことがある。namedのスタートアップスクリプトはマウント先のディレクトリ(この場合は/var/named/chroot/var/named/)が空であることを確認しており、もし余計なファイルがあるとスタートアップスクリプトがエラー終了し、namedが動作しない。namedが動作しないとあとに続くいろいろなサービスが正常動作せず、結局スタートアップそのものが滞ってしまいシステムが起動できなくなることがあった。

スタートアップのログを見て問題はnamedにあるとの大よその見当はついたので、FedroraのインストールDVDをレスキューモードで起動してHDD上の/var/named/chroot/var/named/named.dnsblを削除してシステムを再起動したらあっさり動いた。持ちおrンスクリプトを変更してマウント先ではなくマウント元を更新するようにした。

と言うと簡単に片付いたように聞こえるが、実は先代のサーバハードウェアから引き続き使っていたPATAのDVDドライブがDVDを読み込めず、山ほどある在庫から持ってきたもう1台のDVDドライブも動作せず、3台目のドライブにして漸く正常動作をしたという逸話付きだ。いやあ大変な数時間だった。

2014年4月24日

ディスクイメージファイルをマウントする

Raspberry Pi(RPi)をQEMUエミュレータで走らせている。

QEMUには専用カーネルファイルとディスクイメージファイルを与えるのだが、ディスクイメージファイルは自前のRPiの実機のSDカードイメージその物。

ここでちょっと困ったことに気づいた。QEMU用カーネルと実機のカーネルのバージョンが違うのだ。前者は3.10.26+で後者は3.10.24+。自前のディスクイメージファイルには3.10.26+のカーネルモジュールがないので、カーネルモジュールがロードできない。

RPiの公式ダウンロードページに各種ディストロのディスクイメージファイルが掲載されているでの、このディスクイメージファイルに3.10.26+のカーネルモジュールが入っていればそれをQEMUのターゲットファイルシステムにコピーすればよいと考え、我が家のシステムのベースであるRaspbian(Debian Wheezy)をダウンロードする。このディスクイメージは伸長時約2.8GBが約780MBのZIPファイルに収まっている。

さてダウンロードに成功したものの、これはディスクイメージファイルなのでそのままではマウントできない。「loopデバイスを使えば」と言うかもしれないが、「マウント」とはファイルシステムをマウントすることであり、イメージファイルをloopデバイスでブロックデバイスに見せかけても、このディスクイメージファイルは複数パーティションに分かれており、その中のパーティション(=ファイルシステム、この場合)にアクセスできなければマウントできない。一旦SDカード(でも何でも、とにかく物理ブロックデバイスメディア)に書き出して物理的にマウントなどはやりたくない。

例によってぐぐったら見つかった。 ディスクイメージファイルから目的のパーティションの位置を探し、その情報をmountoffsetオプションに与えればよい。パーティション情報を見るにはpartedを使う(MBRパーティションの場合)。fdiskはバイト単位の表示ができない。

h@spice:~/projects/rpi$ parted 2014-01-07-wheezy-raspbian.img
WARNING: You are not superuser.  Watch out for permissions.
GNU Parted 2.1
Using /home/h/projects/rpi/2014-01-07-wheezy-raspbian.img
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit B                                                         
(parted) print                                                          
Model:  (file)
Disk /home/h/projects/rpi/2014-01-07-wheezy-raspbian.img: 2962227200B
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start      End          Size         Type     File system  Flags
 1      4194304B   62914559B    58720256B    primary  fat16        lba
 2      62914560B  2962227199B  2899312640B  primary  ext4

(parted) q

h@spice:~/projects/rpi$ sudo mount -o loop,offset=62914560,ro,noload 2014-01-07-wheezy-raspbian.img w/

noloadオプションは、マウント時にEXT4のジャーナル情報を無視するためのおまじない。これがないとマウントエラーになりdmesgが「EXT4-fs (loop0): INFO: recovery required on readonly filesystem」と文句を言うことがある(本稿はこちらを参考にした)。なお、当然のことながら、QEMUで使用中のディスクイメージを書き込みモードでマウントするとキャッシュコヒーレンシが崩れてディスクイメージに損傷を与える可能性があるので避けるべし。

実際にダウンロードしたディスクイメージファイルは3.10.25+カーネル用のもので、目的とする3.10.26+は含まれていなかった。目的は達成できなかったが、今のところQEMUで走っているシステムが実際にカーネルモジュールをロードしようとしてはいなさそうなので、これでよしとする。

loopデバイス作成コマンドのlosetupにもoffsetオプションがあるので、これを使えば、例えばパーティションに区切られたディスクイメージファイルからRAIDを再現してマウントなどができそうなことが分かった。


2014年4月23日

Raspberry PiをQEMUエミュレータ上で走らせる(2) 立ち上げ、X Window、ネットワーク

前回でQUEMが動き、エミュレータ上でカーネルとシェルが動き、基本的な動作が確認できた。次は、これを開発に必要な実用レベルに持って行く。

QEMU版への移行

まず、RPiには標準ではバッテリバックアップ付きのリアルタイムクロックはなく、この段階ではNTPDはおろかネットワークも動いていないので、時計が例の1970年元旦になっている。このままでも致命的ではないが、以下に述べるファイル修正の履歴(タイムスタンプ)が後日奇妙に見えるので、dateコマンドでそれらしい時刻を設定する。

root@rpi:/# date 04221250

次に、ホスト名がディスクイメージのコピー元と同じだと紛らわしいのでエディタで変えておく。エディタはディスクイメージにインストールされているものなら何でも使えるが、viが多少不安定な挙動を示したので、最悪nanoで。

root@rpi:/# cat /etc/hostname
rpi-qemu

余計なライブラリのプリロードを禁止。

root@rpi:/# cat /etc/ld.so.preload
#/usr/lib/arm-linux-gnuebihf/libcofi_rpi.so

最後に、必ずしも必要ではないが、実機とディスクデバイスの名前を揃えるためのudevルールファイルを作る。

root@rpi:/# cat /etc/udev/rules.d/90-qemu.rules
KERNEL=="sda", SYMLINK+="mmcblk0"
KERNEL=="sda?", SYMLINK+="mmcblk0p%n"
KERNEL=="sda2", SYMLINK+="root"

で、一度システムをシャットダウン。

root@rpi:/# halt

ホスト側のQEMUを^Cで殺し、もう一度本チャン起動してシステムをフル稼働にする。

h@spice:~/projects/rip$ qemu-system-arm -kernel kernel-qemu -cpu arm1176 -m 256 -M versatilepb -no-reboot -serial stdio -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" -hda rpi.img -redir tcp:2222::22

パラメタについていくつか…
-m 256 システムメモリは256MBがハードコードされているのでこれより大きい値は指定するな、と言われている。
-M versatilepb QEMU上の仮想プラットフォーム。実機ではBCM2708。
-hda rpi.img 最初の立ち上げに使ったのと同じ自前のディスクイメージファイル。
-redir tcp:2222:22 ホストのTCPポート2222をQEMUターゲットのポート22にリディレクトする。SSH用(後述)。

Xウィンドウ


これはあっけなく動いた。実機からコピーしたディスクイメージを使っているので、実機とほぼ同じ。ただし、QEMUの問題と思われる以下の問題が発見された。
・マウスポインタがホストから見える「物理ポインタ」(四角い小さなドット)とターゲット(RPi)に見える論理ポインタ(矢印、Iビームなど)が乖離する。論理ポインタがQEMUのウィンドウの外には出られないクリッピングを起こすので両者を近づけることはできるが、そのうちまた離れてしまうので煩わしい。VNCクライアントなどでもこの兆候はあるが、あちらは自分でできるだけ一致させるように働くのに、こちらはその努力が見えない。
・ロカール(locale)が正しくないのか、ASCIIキーボード「@」や「|」のキーが違うコードを出力している模様。まるでJIS配列キーボードの感じ。
・上下の矢印キーが上手く認識されない(左右の矢印キーはOK)。スクリーンエディタは別のキーを使えるが、bashでリコールバッファが使えないのは痛い。

2番目と3番目のキーコードの問題はロカールとかXの設定ファイルをいじれば動きそうな感じもするが、小生はこの辺が不得手であり、また元々現在の時計プロジェクトでは入力UIは不要なので詮索しない。キーが正常に動作したとしても、マウスポインタの問題でこのUIを使っての開発は実際的ではない。

ネットワーク

これも考えるより産むが易し、と言うか、考えと違う形であっけなく動いた、と言うより動いていた。

以下のスタートアップファイルがあることを確認する。

root@rpi:/# cat /etc/network/interfaces
auto lo

iface lo inet loopback
iface eth0 inet dhcp

ここで実現されるネットワークは、ターゲット側は仮想イーサネット。ホスト側のQEMUのユーザコードがNAT付きラウタ、DHCPサーバ、DNSサーバを実現する閉じたLAN。ターゲットは常に10.0.2.15、ホスト側仮想ゲートウェイは10.0.2.2、仮想DNSサーバは10.0.2.3を与えられるようだ(ターゲットの/etc/resolv.confはNetwork Managerが自動的に書き換える)。ホスト側の仮想DNSサーバはキャッシュオンリーなのでホスト側では何も特別なことをする必要はない。また、DHCPのベースアドレスはQEMU起動時のオプションで設定できるようだが試していない。

このNAT付きラウタというトポロジーのため、QEMUホストがファイアウォールとなり、QEMUホスト以外の他のネットワークホストからはターゲットは見えない。また、ラウタは純粋にユーザコードで実現されているので、QEMUホストの他のプロセスから見えるネットワークデバイスなども存在せず、ターゲットに対するトリッキーなラウティングとかも不可能。

従って、QEMUで走るターゲットをサーバとして使うアプリケーションの開発には向いていない

唯一の抜け穴は、QEMU起動時に指定した -redir tcp:2222::22 でリディレクトされたTCPポート。QEMUホストのポート2222にアクセスすればターゲットRPiのポート22にアクセスしたことになるから、QEMUホストが見えるところならどこからでもターゲットでSSHでアクセスできる。

この辺のことは、5年ほど前にAndroidタブレットの立ち上げの仕事(OSはまだDonutだった)をしていたときに使っていたはずなのに、すっかり忘れている。加齢とともに記憶の揮発性が高まってきたのか…。

逆に、ターゲットからはQEMUホストが見えるところならどこでもアクセスできる。ただし、アクセスされた方はアクセス元はQEMUホストに見えるので注意。例えば、ターゲットからQEMUホストにSSHでアクセスすると、SSH_CONNECTIONは127.0.0.1(localhost)からアクセスされたと主張する。うちのシステムはセキュリティのためにSSHのアクセス元でフィルタリングをしているの修正が必要だった。

もう一つ気がついたことは、QEMUのラウタはICMPを通していないようだ。例えば、ターゲットからwww.yahoo.comにpingすると、IPアドレスは解決するのにpingその物は到達できないという現象に2時間悩んだ。

なお、設定によってはターゲットのコンソールがQEMUを起動したセッション(シェルウィンドウ)にリディレクトされ、loginプロンプトが出ることがあるが、これはよほどのことがない限り使わない方が無難。^Cを押すとコンソールに食われるのではなく、QEMUプロセスを殺してしまう。

さて、これで実機とほぼ同等の動作をするエミュレータができたので開発がし易くなる。仕事が増えた。

なお、QEMUでエミュレートされるCPUはあまり速くない。/proc/cpuinfoには600 BogoMIPSが表示されるが、ターゲットのX上でマウスを振り回すだけでtopのCPU使用率は100%になってしまう。だからリアルタイム性とかの確認は当然のことながら実機ですべき。

2014年4月22日

Raspberry PiをQEMUエミュレータ上で走らせる(1) QEMUのインストールとカーネルの起動

Raspberry Piを使ったCaller ID+天気予報付きディジタル時計プロジェクトを加速することにした。

今回は小生の通常の「字ばっかり」のプロジェクトと違ってXウィンドウを使ったUI(表示のみ)なので、表示具合を確認しながら進めなければならないが、実機がないと開発できないというのはやりにくい。そこで、QEMUを使って実機なしでエミュレートすることを計画した。

RPi用のQEMUについては、こちらこちらの記事を参考にしたが、(当然のことながら)記事のとおりに進めてもその通り動作しない。以下にカットアンドトライで試した結果を報告する。

QEMUのインストール

まずはQEMUのインストールから。ホストは我が家の何でも屋のFC13(x86 32bit 8GB)。他のディストロでは少々違いがあるかもしれないが、そこは工夫してください。
 
上記記事によると、ほとんどのディストロの標準パッケージのQEMUはRPiのCPU(arm1176)をサポートしておらず、また実際にサポートしていないことが確認できたので、パッケージのインストールは諦め、ソースから作ることにする。

まず、適当な作業用ディレクトリを作り、そこにQEMUのGITをクローンする。

h@spice:~/projects$ mkdir rpi
h@spice:~/projects$ cd rpi
h@spice:~/projects/rpi$ git git://git.qemu-project.org/qemu.git

あとは普通にconfigureとmakeを走らせる。上記記事ではPREFIXを「/usr」にしているが、標準パッケージとのガチンコを避けるためにデフォルトの「/usr/local」にしておく。「git submodule update --init dtc」は、configureがDTC(libfdt)が見つからないと文句をいい、「libfdt-devel」パッケージが見つからなかったので。

h@spice:~/projects/rpi$ cd qemu
h@spice:~/projects/rpi/qemu$ git submodule update --init dtc
h@spice:~/projects/rpi/qemu$ ./configure --target-list="arm-softmmu arm-linux-user"    \
            --enable-sdl    \
            --prefix=/usr/local
 
・・・

h@spice:~/projects/rpi/qemu$ make
・・・

h@spice:~/projects/rpi/qemu$ sudo make install

上記記事では、ここで「qemu-system-arm -cpu help」でRPiのCPU(arm1176)がサポートされているかどうか確認することになっているが、これが上手くいかない。代わりに「qemu-arm -cpu ?」は動いたので、これでよいことにする。

h@spice:~/projects/rpi/qemu$ qemu-system-arm -cpu help
No machine specified, and there is no default.
Use -machine help to list supported machines!

h@spice:~/projects/rpi/qemu$ qemu-arm -cpu ?  
Available CPUs:
  arm1026
  arm1136
  arm1136-r2
  arm1176
・・・

RPiのイメージファイル

次は、また適当な作業ディレクトリを作って、カーネルイメージとディスク(SDカード)イメージファイルを作る。
カーネルイメージファイル: 自前のカーネルはダメ。以下のように専用カーネルをダウンロードする。
ディスクイメージファイル: 動作するディスクイメージ(ファイルシステムイメージではない)なら何でもよい。ここでは実機「rpi」からコピーしてみた(このコピーは時間がかかるので先行してバックグラウンドで実行するとよい)。

言い忘れたが、この作業ディレクトリはディスクイメージファイル(小生の実機は8GB)を 作るので、ホストに十分なディスク空き容量があることを確認しておく。

h@spice:~/projects/rpi$ mkdir rpi-emu
h@spice:~/projects/rpi$ cd rpi-emu
h@spice:~/projects/rpi/rpi-emu$ wget http://xecdesign.com/downloads/linux-qemu/kernel-qemu
h@spice:~/projects/rpi/rpi-emu$ ssh rpi if=/dev/mmcblk0 bs=16M >rpi.img

イメージファイルができたらQEMUを起動する。

h@spice:~/projects/rpi/rpi-emu$ qemu-system-arm -kernel kernel-qemu -cpu arm1176 -m 256 -M versatilepb -serial stdio -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw init=/bin/bash" -hda rpi.img
audio: Could not init `oss' audio driver
Uncompressing Linux... done, booting the kernel.

お馴染みの「Uncompressing Linux...」が表示され、デスクトップにRPiのコンソールウィンドウが現れるので嬉しくなる。

ここでのキモは、カーネルコマンドラインの「init=/bin/bash」で、ユーザレベルの初期化を何もせずにいきなりbashを起動する

もし何らかのエラーがありカーネルがパニックになると、上記のコマンドラインでは自動的にリブートを繰り返す。「-no-reboot」オプションを追加すると、リブートせずにQEMUが終了する。いずれにせよデバックがやり辛いので、パニックのメッセージがコンソールに表示されたらコンソールウィンドウの MENU>PAUSE でウィンドウを止める。残念ながらこのウィンドウはスクロールできないようなので(もしかしたらできるかもしれないがよく分からない)、PAUSEは上手くタイミングを計らねばならない。

下はわざとエラーを起こして見たところ(ディスクイメージファイルが完成しないうちにマウントさせようとした)。


順調に行けば、bashのプロンプトが出てきて「やった~!」となるが、世の中そう甘くはない。起動しただけの状態では、言わば丸裸で、/procさえマウントされていないから、色々なシステムユーティリティが動かない。取りあえずちょっとだけやってみる。


ダミーのネットワークインタフェースが見えるが、MACアドレスはわざとらしい「~00:12:34:56」。カーネルのバージョンは、実機の3.10.24+ #614より新しそう。

取りあえずカーネルは動き、シェルが動くようになった。次はここでネットワークとXウインドウを動かすが、それは次回に。

おっと、一言。QEMUのコンソールウィンドウは、クリックすると以降のマウスクリックやキーボードの入力をすべてトラップするので、意図せずにトラップされた時は、パニックらずに「Ctrl+Alt+G」で逃げ出す。