2015年7月17日金曜日

Lavie Direct HZ に Arch Linux をインストール

大まかな流れは Installation guide - ArchWiki に沿えばいいのですが、幾つかハマりポイントがあったのでメモを残しておきます。

起動メディアの準備

Lavie Direct HZ(以下 Lavie)には DVD ドライブなどは無いので USB 経由の何かで起動することになります。今回は USB メモリでやることにしました。

USB メモリへの書き込みは Silicon Linux さんの DD for Windows とか John Newbigin さんの dd for windows などを利用すれば簡単です。これらのツールは管理者権限で起動するのがポイントといえばポイントでしょうか。

インストーラの起動

工場出荷時の設定にも依るのでしょうが、僕が買ったマシンでは UEFI が USB 系のメディアより内蔵 SSD を優先して起動する設定になっていたので、 Lavie 起動画面で F2 を押して UEFI の設定画面に行き、起動優先度を変更しておきます。

さらに(少なくとも僕のマシンでは) UEFI のセキュアブートが有効になっているので、先ほど準備した USB メモリを挿して電源を入れても「Failed to Start loader」というエラーが出てインストーラが起動しません。セキュアブートを無効にしておきましょう。

※ Unified Extensible Firmware Interface - ArchWiki では loader.efi と vmlinuz.efi に対して Enroll Hash (ハッシュ値を登録)を実行して回避する手順が載ってますが、後々で別のエラー(Secure Boot Violation)が出ますので、セキュアブートを無効にしてしまうのが手っ取り早いでしょう。


フォントの調整

インストーラが起動して最初に思うのは、きっと「フォントちっさ!」だと思います^^;
Lavie は解像度が高すぎ、普通のフォントだと小さすぎるわけです。目が疲れてしまいますので、大きいフォントをインストールしましょう。My fonts are too tiny で紹介されている Terminus font を入れることにします。

まず、ワイヤレスネットワークの設定を済ませます。Installation guide の "Connect to the Internet" を参考に wifi-menu コマンドを使えば簡単に設定できます。

インターネットアクセスができるようになったら pacman のミラーリストで日本のミラーを一番上に持っていきます(ミラーの設定は必須ではないですが pacman の動作速度に大きな影響があります)。その後 pacman -Syy でパッケージリストを更新します。更新が完了したら pacman -S terminus-font でフォントをインストールしましょう。 /usr/share/kbd/consolefonts/ 以下に ter-xxx.psf.gz みたいなフォントファイルがインストールされるはずです。

最後にフォントを適用します。 setfont ter-128n (28 がサイズを表します。Terminus font の最高は 32 のようです。)でちょうどいい大きさのフォントになるはずです。128b とか 228n/b とか u28n/b みたいなフォントファイルもあるようですが、違いは分かりませんでした。

ブートローダの優先度設定

UEFI ブートをしたかったので GRUB を選びました。普通に GRUB をインストールし grub-mkconfig した後再起動しても Windows のブートローダが立ち上がってしまうので、 UEFI の設定画面(起動時に F2 を押すやつ)の Boot タブの "Hard Disk Drive BBS Priorities" から優先度を変えてやります。1st Boot として arch_grub を指定してあげれば OK です。

efibootmgr コマンドを使うとあたかも優先度を変更できそうなのですが、 Lavie の UEFI 実装と相性が悪いのか、再起動すると優先度設定が元に戻ってしまうようです。

タッチパッドが(時々)効かない

僕の場合はカーネルの起動オプションに i8042.reset を付け加えることで解決できました。/etc/default/grub の GRUB_CMDLINE_LINUX_DEFAULT に i8042.reset を加え、 sudo grub-mkconfig -o /boot/grub/grub.cfg とします。

以下、解決するまでのストーリー

タッチパッドが効くときと効かないときの xinput コマンドの出力を比べてみると、デバイスが検出できているかどうかの問題切り分けができます。僕の場合、この時点でデバイスが検出できてないことが分かっていました。

また、何度も再起動を繰り返す中で、タッチパッドが使えない時は必ず起動後の 1 文字目のキーボード入力が捨てられることも判明していました。ログイン画面でユーザ名を入力する際、 1 文字目が入力できないのです。したがって、キーボードコントローラ周りの初期化にミスってるのではないかと見当を付けて解決法を探しました。

解決に導いてくれたのは Touchpad not recognized on new Ultrabook という質問でした。この中の回答で i8042.nomux オプションを付けたら解決するかも、という記述があり試しました。結果としてこのオプションは僕の環境では効果はありませんでした。しかし i8042.nomux をキーワードに検索してみると What does the 'i8042.nomux=1' kernel option do during booting of Ubuntu? という質問に辿り着き、 i8042.reset の存在が分かりました。

数回再起動実験をしていますが、今のところすっかり直っている感じです。

2015年6月16日火曜日

Linux Network Namespace メモ

勉強資料

Introducing Linux Network Namespaces
チュートリアル形式で基礎が学べる

Linux Switching – Interconnecting Namespaces
veth ペアの基礎や Linux Bridge/Open vSwitch での 2 つの名前空間の接続が図入りで解説されている

Linux Network Namespace で遊んでみる
具体的なコマンド例が多数

network lxc Linux Containers
LXC とネットワーク名前空間を関連させた話
「ネットワーク名前空間は1つ以上のプロセスに割り当てられたプライベートなネットワークリソースの集合」

ifconfig vs ip
ifconfig は deprecated になりこれからは ip の時代。ということで具体例でコマンドの比較。

LXC と ネットワーク名前空間

veth ペアの片側を LXC コンテナの名前空間に移動させる

# lxc-info -n NAME
でそのコンテナの init の PID が分かる。その PID に対して
# ip link set VETHNAME netns PID
とやれば veth ペアの片側(VETHNAME で指定した側)が PID が属する名前空間に移動する。

移動後に確認するには下記コマンドを実行してみる。
# lxc-console -n NAME
でコンテナに入り込んでから
[root@arch1 ~]# ip link
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth-arch:  mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 3e:a5:f7:ed:54:9a brd ff:ff:ff:ff:ff:ff link-netnsid 0
みたいに VETHNAME がちゃんとコンテナに移動したことが分かる。(この例では VETHNAME = veth-arch)

veth ペアの片側を元の名前空間に引き戻す

普通、ある名前空間から別の名前空間にネットワークインタフェースを移動するには
# ip netns exec 移動元名前空間 ip link set インターフェース名 netns 移動先名前空間
としてやればよい。が、移動元名前空間が ip netns add 以外で作ったもの(LXC の名前空間はまさにそれ)である場合、名前空間に名前が付いてなくてコマンドが実行できない。

ということで、まずは一時的な名前空間名を付与してあげる。
# ln -s /proc/PID/ns/net /var/run/netns/NSNAME
PID はコンテナの PID で NSNAME は任意の名前空間名。/proc/PID/ns/net が「PID が属する名前空間」を表すファイルになっている。

とにかく /var/run/netns/ 以下に名前を付けてあげると ip コマンドが認識するという仕組み。(ここでは NSNAME = ns-arch1)
# ls /var/run/netns
ns-arch1
# ip netns list
ns-arch1 (id: 0)

こんな感じに名前が付けば、あとは ip netns exec コマンドを使うだけ。
# ip netns exec NSNAME ip link set VETHNAME netns NSNAME_TO

NSNAME_TO にはもちろん PID も指定できて、つまり 1 を指定してあげると元々の名前空間に VETHNAME が戻ってくる。親元に帰ってくる子どもみたいな感じ。

個人的には ip netns exec NSNAME にも PID を指定させてくれればいいのに、と思うけどなぜかそれは出来ないので、リンクを張るという技を使わざるを得ないようだ。
作ったリンクは iproute2 も LXC も管理してないものなので、必要が無くなったら手動で削除しておくのが望ましい。
# rm /var/run/netns/NSNAME

以上の知識はツイッターで教えていただいた。ありがとうございます。
https://twitter.com/Flast_RO/status/610953725054615552

2015年5月31日日曜日

Qt Creator で Qt Quick アプリを作るときの落とし穴回避 & TIPS

接続した Android タブレットが「互換性のないデバイス」と表示されてしまう

その Android が armeabi-v7a アーキテクチャのときに Android for x86 などのキットを使うと互換性がない。

プロジェクトのプロパティ画面で、対象のアーキテクチャ用のキットを追加する。
目的のアーキテクチャ用のキットが見当たらない場合は maintenancetool.exe で追加インストールする。
maintenancetool.exe はスタートメニューの Qt の中にショートカットがあるはず。Qt のインストールディレクトリから直接起動してもよい。

QML ファイルを追加したけど、変更しても反映されないことがある

QML ファイルが Makefile の依存関係に取り込まれていない可能性があるので「ビルド」 >「qmake の実行」で Makefile を更新してみる。

2015年4月5日日曜日

読みやすいソースコードを書く方法

読みやすいソースを書くために気をつけていることを軽くまとめてみます。ソースコード例は Python ですが、特に言語に関係なく通用する話題です。業務などでコーディングする際の参考になれば幸いです。

良い名前を付ける

最優先で気をつけるのはこれです。「プログラミングは『名前』が9割」と言われることもあるくらい、名前付けには時間を割くべきだと思います。良い名前を付ければプログラムの見通しが良くなり全体像を把握しやすくなりますし、他人(または未来の自分)が保守しやすいソースコードになります。

では良い名前とはなんでしょうか?自分なりにまとめてみました。

名前と内容を一致させる

関数名なら処理内容を端的に表す名前にすべきですし、逆に名前から想像できない処理はしてはいけません。

私は実際に、副作用を持った operator +() の実装を見たことがあります。内部でメンバ変数を書き換えていたのです。 operator +() は C++ の機能で、自作クラスに関する加算(a + b)を独自に定義できるのですが、加算演算子ですからもちろん(外から見える)副作用を持つべきではありません。これは、関数が名前から想像できない処理をしてしまっている例です。

動的型付け言語ではもっと注意が必要なことがあります。例えば get_host() という関数を見たとき、これが Host クラスのインスタンスを返すのか、ホスト名を文字列として返すのか、新人さんは迷うはずです。ただ get_host() 自体は適切な関数名ですから、これを変えるのは難しいです。それより、ホスト名を扱う変数は常に host_name という名前にする、 get_host() の戻り値を host_obj という変数で受け取るなど、ホスト名と Host インスタンスを区別するように意識しましょう。

boolを返す関数はcheck…にしない

例えば hoge.check_available() だと hoge が有効なのが戻り値が真のときか偽のときか分かりません。 hoge.is_available() であれば紛れはありません。if 文の条件に書いたときに英文として自然になる名前を選ぶようにすると良いでしょう。

is, has など英語の三人称単数形を使うと適切なことが多いですが、敢えて動詞を使わないこともあります。例えば C++ のコンテナには、要素が空かどうかを調べる関数 empty() があります。これは形容詞だけの名前ですが、下手に check_empty() とするより分かりやすい関数名ですね。

コメントを付ける

これも基本中の基本でしょう。コメントはソースコードの理解を大幅に助けてくれますし、自分が後で読み返すときも、ソースコード断片を一つのかたまりとして思い出すのに大いに役立ちます。

ただ、闇雲にコメントを書けばよいかというとそうでもありません。多くの入門書にも書いてあることですが、コメントにも悪いコメントと良いコメントがあります。一般に、ソースコードから一瞬で分かることをコメントで書いてはいけません。関数コメントの例で言えば、get_size() に対して「サイズを返す」とコメントするのは単なるタイプ数の無駄というものです。関数名だけで十分な情報は伝わっていますから。一方で get_host() に対し「Host クラスのインスタンスを返す」とコメントするのは価値があります。この関数がホスト名でなく Host クラスのインスタンスを返すことが分かるからです。

一見して分かる情報はコメントしないという一般原則を頭に置きつつ、以下ではコメントのパワーが最大に発揮できる場面を考えていきましょう。

関数コメント

関数コメントは本当に役立ちますので、是非書いて下さい。関数コメントは普通、その関数の利用者に向けて書くものですが、関数の実装者にとってもその関数をより良いものにするために役立ちます。容易くコメントが書ける関数は、利用者にとっても直感的に使える関数になりやすく、長く複雑なコメントを書かねばならない関数は、利用も困難な関数になりがちです。関数の利用しやすさを図る尺度として、関数コメントが役立つのです。

関数コメントには最低限、その関数が何をするかという一言を書いて下さい。簡単な関数ならそれだけで十分です。少し大きな関数には、引数と戻り値の意味をコメントします。実践に則した関数の使用例があるとなお良いです。Python なら関数コメントに実行例を含めて python -m doctest hoge.py などと自動テストができますから、そのテストに合格するように書くと完璧です。

def fetch_hosts(conn, klass):
    '''class が `klass` であるホストオブジェクトすべてをテーブルから読み込んで返す。

    conn    - DB コネクションオブジェクト
    klass   - Host クラスのサブクラスオブジェクト

    >>> conn = MySQLdb.connect(host='127.0.0.1', db='hostdb')
    >>> hosts = fetch_hosts(conn, AppHost)
    >>> conn.close()
    '''

    # do something

ソースコードコメント

ソースコード断片に対してコメントを書くことも可読性の向上に役立ちます。ソースコードを意味のまとまりごとに空行を入れて分け、それぞれのソースコード断片に対して1行から数行のコメントを書くと調度良い感じです。コメントは、その断片が何をしようとしているのか、意図を書くようにします。例えば、注文履歴をもとに各商品が何個売れたかを計算するプログラムにコメントを書いてみます。

# 昨日の注文履歴をすべて取得する。
begin_dt = date.today() - timedelta(days=1)
end_dt = date.today()
sells = fetch_sells(conn, begin_dt, end_dt)

# 商品ごとの注文数をカウントする。 { 商品名 : 注文数 }
item_sells = defaultdict(int)
for sell in sells:
    item_sells[sell.item_name] += sell.count

このように、それぞれのソースコード断片が何を目的としているかを簡単に書いておくと、他人がソースコードを理解するときにも、自分が後で処理の全体像を読み返すときにも役立ちます。プログラムを単に日本語に変換しただけのような、ソースコードを読めば分かるコメントにならないように注意してください。

グローバル変数を使わない

グローバル変数やシングルトンなど、どこからでも参照できる変数があるとソースコードが読みにくく、保守しにくくなる場合が多いです。あるソースコード断片が上手く動くかどうかがその変数の状態に依存する場合、分かりにくいバグの原因となります。例えば、DBに接続してユーザ一覧を取得するプログラムを考えます。

setup_db()
user_db = UserDB.instance()
users = user_db.get_users()

setup_db() は UserDB.instance() を初期化してデータベースに接続する関数です。User.instance() はシングルトンの実装になっていますので setup_db() はあるプロセスの中で1回だけ呼び出せば良く、他の場所でユーザ一覧が欲しいと思えば以下のように書くことになります。

# setup_db() ?
user_db = UserDB.instance()
users = user_db.get_users()

ここで、プログラマは setup_db() を呼び出すべきかどうかを考えます。もし、この部分より前で setup_db() が呼び出されるなら、基本的にはここで呼ぶ必要はありません。しかし、もしこの2行が直前の setup_db() の呼び出しと別プロセスで動くなら、再度 setup_db() を呼ぶ必要があります。別プロセスで動くかどうかはこの2行からは判断できず、プログラム全体のアーキテクチャを理解する必要があります。この例では、グローバル変数の使用が保守性を著しく低下させています。

2015年1月31日土曜日

bossac で Arduino Due に書き込む

Mac OS X Mavericks のターミナルで Arduino Due にプログラムを書き込む方法です。
getting-started_flash.bin をフラッシュメモリに書き込んでみます。

stty -f /dev/cu.usbmodem1411 1200
/Applications/Arduino.app/Contents/Java/hardware/tools/bossac -U false -p cu.usbmodem1411 -e -w -v -b getting-started_flash.bin -R

bossac で Arduino Due に書き込む前に stty コマンドで通信速度を 1200 bps に設定するのがミソです。これをやらないと "No device found on cu.usbmodem1411" というエラーが出てしまいます。

サンプルプログラムの準備

ちなみに、書き込むための bin ファイルは ASF (Atmel Software Framework) に付属のサンプルプログラムを GCC でビルドしました。ダウンロードページに "ASF standalone archive for GCC (makefile-based) ..." というリンクがありますので、そこからダウンロードして展開します(執筆時点での最新版が 3.20.1 でした。以下 ~/Downloads/xdk-asf-3.20.1 に展開したとします)。

$ export PATH=$PATH:/Applications/Arduino.app/Contents/Java/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin/
$ cd ~/Downloads/xdk-asf-3.20.1/sam/applications/getting-started/sam3x8e_arduino_due_x/gcc
$ make
/Applications/Xcode.app/Contents/Developer/usr/bin/make all PROJECT_TYPE=flash
CC      common/services/clock/sam3x/sysclk.o
MKDIR   common/services/serial/
CC      common/services/serial/usart_serial.o
MKDIR   common/utils/interrupt/
...
$ ls *.bin
getting-started_flash.bin getting-started_sram.bin

まず Arduino IDE 付属の GCC コマンドへのパスを通します。
パスを通したら展開したソースコードの sam/applications/getting-started/sam3x8e_arduino_due_x/gcc に移動します。
最後に make を実行すると、カレントディレクトリに getting-started_flash.bin が出力されるはずです。

ASF には Arduino Due で使えるサンプルプログラムが幾つか入っています。以下のように探せます。

$ cd ~/Downloads/xdk-asf-3.20.1/sam
$ find . -name Makefile | grep arduino
./applications/getting-started/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/adc/adc_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/adc/adc_temp_sensor_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/adc/adc_threshold_wakeup_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/chipid/chipid_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/dacc/sinewave_example/sam3x8e_arduino_due_x/gcc/Makefile
./drivers/gpbr/unit_tests/sam3x8e_arduino_due_x/gcc/Makefile
...

2015年1月19日月曜日

同じval++;でもaddになったりincになったり。

最近GCCのビルドをたくさんやってるなかで気づきました。
int val = 0;
val++;
という単純なC言語プログラムでさえ、i386向けのGCCとi686向けのGCCで出力されるバイナリが違うことを。

違うのは変数のインクリメントでした。i386向けGCCは
inc dword [ebp-4]
を出力しました。一方でi686向けGCCは
add dword [ebp-4], 1
を出力したのです。

addの方がバイナリが大きくなるのですが、なぜi686向けはそちらを選択したのか。僕はその理由を知りません。不思議です。

2015/01/20追記
Twitterで良い記事を紹介していただきました。
この記事によれば、inc/dec命令でストールが発生するアーキテクチャがあるそうな。そこで、そういうアーキテクチャではincの代わりにaddを使うんだそうです。
x86アーキテクチャの闇は深い。

2015年1月16日金曜日

Python の exit(), sys.exit(), os._exit() の違い

Python には 3つの似たようなプログラム終了用の関数があります。
exit(), sys.exit(), os._exit()です。
これらの違いを簡単に調べてまとめてみました。

ものぐさな人へ


  • インタラクティブシェルを終了するには exit()
  • スクリプトの中でメインプロセスを終了するには sys.exit()
  • fork() した子プロセスを終了するには os._exit()


exit([code=None])

site モジュールにより、プログラムの起動時に自動的に追加される定数。
exit 自体が表示されると画面に "Use quit() or Ctrl-D (i.e. EOF) to exit" のようなメッセージを表示し、
exit() と呼び出すと、終了コードを伴って SystemExit 例外を投げる不思議なオブジェクト。

sys.exit() にも言えるが、例外を投げるだけなので、その外側で例外を捕捉してシステムを終了させないようにすることも可能。

sys.exit([arg])

終了コードを伴って SystemExit 例外を投げる関数。
つまり、上で紹介した exit() と呼び出し時の挙動は同じ。

os._exit(n)

終了ステータス n でプロセスを終了する。
これは exit(), sys.exit() と違って、例外を投げるのではなくプロセスを終了させる。
os._exit() は fork() された後の子プロセスを終了させるときに使うのが標準的。