2011年12月21日水曜日

ハッカーセントリックな企業文化

TechLION vol.5に行ってきた:

http://www.usptomonokai.jp/wp/?p=2068

自称プロの酔っぱらいである楽天のよしおかさんが、「ハッカーセントリックな企業文化」について語るということだった。Twitterを見ていると、すでに楽屋でビールを飲んでおられたようだった。さすがプロである。

よしおかさんはこう言う:
良いソフトウェア、良いシステムを作りたいなら、最高のプログラマを雇うべき。見も蓋もない話なんですよ。
ここで言う「最高のプログラマ」というのが、ハッカーということなのだろう。

では、ハッカー的モノづくりと、非ハッカー的モノづくりを隔てるものは何だろうか? どうして、ハッカーのほうが素晴らしいモノづくりができるのだろうか? これは、ハッカーと非ハッカーがモノづくりに対してとる態度の違いに現れているように思う。

モノづくりにおいて、リスクを回避する最も簡単な方法は、モノを作らないことだ。もちろん、モノがなければシステムは作れないので、実際にはコンポーネントを買ってくることになる。これは、実際に私の現場でも行われていて、COTS(Commercial Off The Shelf)というカッコイイ名前で呼ばれている。

COTSを利用するメリットは、そのコンポーネントに関するあらゆるリスクをベンダーに転嫁できる(と非ハッカーは考える)ことだ。COTSのバグが見つかったら、ベンダーへ怒りの電話を入れればよい。彼らが徹夜で直してくれるはずだ(と非ハッカーは考える)。

ハッカーは、既存のコンポーネントを組み合わせてシステムを作る事自体には反対しない。むしろ、そのようなモノづくりは「巨人の方に乗る」ものとして賞賛されさえする。実際、ハッカーはオープンソースのコンポーネントをよく利用する。問題は、そのコンポーネントに対する「態度」だ。

例えば、あるコンポーネントでバグが見つかったときに、「そのコンポーネントはCOTSなので私には関係ありません」と言って責任逃れをするのは非ハッカー的態度だ。このような言い訳はハッカーとしてのプライドを大きく傷つけるものだし、こういう言い訳をする人間はハッカーではないと他のハッカーから見なされる。

ハッカーがオープンソースを好む理由もここにある。ソースがあって自分でいじくり回すことができるからこそ、すべての責任を取ることができる。すべてをコントロールすることができる。責任逃れはできないのだ。

ここからわかることは、ハッカーは自分の作った製品やサービスに対して非常に責任感が強いということだ。これは、ハッカーの一見自由奔放なイメージとはかなり違う。実は、非ハッカーのほうが責任感がない。非ハッカーは、いかにして責任を取らずに済むかを考える。

だから、もし本当に素晴らしいモノづくりがしたいのなら、ハッカーの態度を真似るべきだ。次に何か責任逃れをしようと思うときがあったら、グッとこらえて「自分の責任」にしてしまおう。自分でデバッグに乗り出そう。そして、いつでもそうできるようにオープンソースを採用しよう。

でも、どうしてもハッカーセントリックな企業文化が自分の会社に根付かないのなら、他のハッカーを探して別の会社に移るというのも手かもしれない。世の中には、ハッカーはたくさんいるはずだ。

2011年12月17日土曜日

xv6を読む:メモリアロケータ

実行時に、プロセスやカーネルにページを割り当てるため、xv6はメモリアロケータを使用する。メモリアロケータは、未使用のページのフリーリストを管理している。カーネルからの要求に従って、フリーリストからページを割り当てる。

カーネルの起動処理で、フリーリストを未使用のページで満たさなければならない。ここで、鶏と卵の問題が生じる。すなわち、フリーリストの初期化処理を行うためにはカーネルに物理メモリが割り当てられていなければならず、カーネルに物理メモリを割り当てるためにはメモリアロケータが動作しなければならない。

xv6では、フリーリストの初期化を二段階で行うことで、これを解決している。main関数から呼び出されるkinit1関数と、kinit2関数がそれである。関数呼び出しの関係を以下に示す:

main
:
+->kinit1
|  +->initlock
|  +->freerange
|     +->kfree
+->kvmalloc(前回説明。kinit1で初期化したページを使用する)
:
+->kinit2
|  +->freerange
|     +->kfree
:


ここで、xv6のアドレス空間について少し説明する。仮想アドレス空間を左に、物理アドレス空間を右に示す:


        4G->+----+
           /|    |\
     Device |    |
           \|    |
0xFE000000->|----|
           /|    |
            |    |
Free Memory |    | Kernel Space
            |    |
           \|    |
       end->|    |        +----+<-4G
            |    |        |    |\
 +0x100000->|    |        |    | Device
  KERNBASE->|    |        |    |/
           /|----|/       |----|<-PHYSTOP
            |    |        |    |\
            |    |        |    |
            |    |        |    |
            |    |        |    | Extended Memory
            |    |        |    |
 User Space |    |        |    |
            |    |        |    |
            |    |        |----|/
            |    |        |    |<-0x100000
            |    |        |    |<-I/O Space
            |    |        |    |<-640k
           \|    |        |    |<-Base Memory
         0->+----+        +----+
            Vertual      Physical


仮想アドレス空間では、カーネルは上位2GBにマップされる(KERNBASE=2GBに定義されている)。したがって、ユーザプロセスは2GBまでのアドレス空間を利用できる。カーネルのテキストとデータは、仮想アドレスでKERNBASE + 0x100000〜endの間にロードされる。これは、物理アドレス0x100000〜end - KERNBASEにマップされている。

物理アドレス空間で、カーネルの後ろ、すなわちend - KERNBASEからPHYSTOPまでの空間をページとして利用する。メモリアロケータは、この部分をフリーリストとして管理する。PHYSTOPは240MBに定義されている。

xv6はブート時のページテーブルとして、entrypgdirを持っている。entrypgdirは、仮想アドレスの0〜4MBを実アドレスの0〜4MBへ、仮想アドレスのKERNBASE〜KERNBASE + 4MBを実アドレスの0〜4MBへマップするよう設定されている。前回説明したkvmallocでカーネルページテーブルが作成されるまでは、カーネルはこのページテーブルを用いてアドレス変換を行う。すなわち、xv6は、カーネルのサイズは少なくとも4MBより小さいものと仮定している。

さて、kinit1では、end〜4MBまでの物理アドレス空間を4KBのページに分割し、フリーリストに登録する。この処理を行うのが、freerange関数とkfree関数である。kinit1は、main関数内で以下のように呼び出されている:

kinit1(end, P2V(4*1024*1024));


ここで、P2Vマクロは物理アドレスを仮想アドレスに変換するマクロである。このマクロは、単に引数 + KERNBASEに展開されるだけである。

freerange関数は、以下の処理を行う:
  1. 引数vstartとvendで渡された仮想アドレス空間(範囲)についてPGSIZE(4KB)ごとにkfree関数を呼び出す
kfree関数は、以下の処理を行う:
  1. 引数vで渡された仮想アドレスが以下の条件にない場合、パニックする:
    • ページ境界に合っていない
    • endより小さい
    • v2p(v)がPHYSTOP以上である
  2. vから1ページ分、メモリ領域を1で埋める
  3. vをフリーリストにつなぐ
ここで、v2p関数は、上記のP2Vマクロの親戚で、仮想アドレスvからKERNBASEを引き、仮想アドレスを物理アドレスに変換するものである。同種の関数・マクロには、他にp2vとV2Pがある。

以上で、フリーリストの初期化の第1段階が完了した。これで、4MBまでの範囲で、メモリアロケータはページを割り当てることができる。

フリーリストの初期化の第2段階であるkinit2関数は、main関数のかなり後のほうで、以下のように呼び出されている:

kinit2(P2V(4*1024*1024), P2V(PHYSTOP));


4MBからPHYSTOPまでの物理アドレス空間を、フリーリストに登録する。以上でフリーリストの初期化はすべて完了し、メモリアロケータを使用することができる。

2011年12月13日火曜日

xv6を読む:アドレススペースの作成

xv6というOSがある。これは、UNIX V6をベースに(?)MITで開発された教育用のOSである。x86アーキテクチャで動作する。MITなどのアメリカの大学が凄いと思うのは、授業の教材や講義の録画をネットで公開してしまうことである。xv6もテキストとソースコードが公開されている:

http://pdos.csail.mit.edu/6.828/2011/xv6.html

xv6のテキストは、解説とソースコードがLions' Commentaryのように2分冊になっている。それぞれ100ページ以下である。テキストを読み進めてみると、xv6は、x86アーキテクチャとその応用であるOSを勉強するのに格好の教材になりそうだ。

そこで、勉強の成果を忘れないようにメモしておくため、そして、もしかしたら私のメモが役に立つという人もいるかもしれないので、このブログでまとめていくことにする。もちろん、ツッコミは大歓迎である。今回は、アドレススペースの作成である。

OSのブート後、main関数が呼ばれる。最初に、main関数では、アドレススペースの作成が行われる。ここでは、仮想アドレスから物理アドレスへマップするページテーブルを作成する。x86での仮想アドレスは3つの部分を持つ。以下に、mmu.hから引用する:

+--------10------+-------10-------+---------12----------+
| Page Directory |   Page Table   | Offset within Page  |
|      Index     |      Index     |                     |
+----------------+----------------+---------------------+
 \--- PDX(va) --/ \--- PTX(va) --/ 

仮想アドレスは、それぞれ10ビットのページディレクトリインデックスとページテーブルインデックス、そして12ビットのページ内オフセットからなる。それぞれの意味を以下に示す:
  • ページディレクトリインデックス:当該仮想アドレスに対応する物理アドレスをマップするページディレクトリエントリを指す
  • ページテーブルインデックス:当該仮想アドレスに対応する物理アドレスをマップするページテーブルエントリを指す
  • ページ内オフセット:上記2つによって指定されるページ内のオフセット(このページのアドレス+オフセットが物理アドレス)
アドレススペースの作成は、main関数からのkvmalloc関数の呼び出しで行う。setupkvm関数でページディレクトリを作成し、それをswitchkvm関数でCR3レジスタに設定する。関数の呼び出し関係を以下に示す:


main
:
+->kvmalloc
|  +->setupkvm
|  |  +->mappages
|  |     +->walkpgdir
|  +->switchkvm
:


setupkvm関数では次の処理を行う:
  1. ページディレクトリ用に1ページ(4KB)を割り当てる
  2. 次の各カーネル領域について、mappages関数を呼び出すことで、ページテーブルを作成し、ページディレクトリエントリに書きこむ:
    • I/O空間
    • カーネル(テキストと読み込み専用領域)
    • カーネルデータ
    • その他デバイス
  3. 作成したページテーブルを、1のページディレクトリに書き込み、返す
ここで、各カーネル領域は任意の大きさでよい。mappages関数に領域のサイズを渡すことで、必要な分だけページテーブルが割り当てられる。

mappages関数は次の処理を行う:
  1. walkpgdir関数を呼び出し、引数で渡された仮想アドレスに対応する、ページテーブルエントリを取得する
  2. エントリにマップする物理アドレス、アクセス権、存在ビットを書きこむ
  3. 領域全体をマップし終わるまで1から繰り返す
walkpgdir関数は次の処理を行う:
  1. ページディレクトリから、ページディレクトリインデックスに該当するエントリを引く
  2. 1で引いたエントリの存在フラグが立っていれば、ページテーブルを取得する(エントリに記述されているアドレスに存在するはず)
  3. 2でない場合、関数の引数でallocフラグに真が渡された場合(この場合はそうである)、新たに1ページ取得し、これをページテーブルとする。このページテーブルの物理アドレス、存在ビット、書き込み可ビット、ユーザ空間ビットを、1で取得したエントリに書きこむ
  4. 2または3で取得したページテーブルから、ページテーブルインデックスに該当するエントリのアドレスを返す
以上で、アドレススペースを作成した。ただし、ここで作成したのはページテーブルまでで、物理ページの割り当ては行っていない。次回は、テキストに従って、物理メモリの割り当てに進みたい。

2011年12月8日木曜日

スキルかセンスか?

ちょっと前の話になるのだが、「デザイナ&エンジニア交流会」というのに参加してきた:

http://design1.chu.jp/setucocms-pjt/?p=1770

SetsucoCMSというオープンソースのCMSがあって、それを作っている子たちによく遊んでもらっている。今回、彼らが勉強会をやるということで、私も誘われた。デザイナやエンジニアと言っても、ここでは「Web」デザイナ、「Web」エンジニアである。Webにとんと疎い私としては、そこはまったくの異文化圏である。そこでの交流は、なかなか刺激的であった。

中でも非常に新鮮だったは、普段接することのないデザイナと話す機会を持てたことだ。そして、彼らと話してわかったことには、どうやら彼らは、デザインがセンスだとエンジニアに思われていることに強い不満を持っているらしい。

確かに、「デザイン」というと、何だか芸術的なものを連想してしまう。実際、私もデザインはセンス、つまり才能によるところが大きいのだろうと何となく思っていた。しかし、デザイナの彼らに言わせれば、デザインには理論があり、練習によって習得できるものであって、したがってデザインはスキルであるという。

ここでちょっと野球を考えてみる。長嶋茂雄さんといえば往年のスター選手だ。あるとき、ホームランの打ち方を聞かれた長嶋さんは、「ボールが来たら、よく見て打て」と大真面目に答えたそうだ。長嶋さんにとっては、それがホームランの理論であり、練習によって習得したということだろう。

デザイナが、デザインがセンスだというエンジニアの思い込みに不満を持つということは、その裏返しとして、彼らは、エンジニアリングはスキルだと思っているのかもしれない。しかし、この思い込みが必ずしも正しくないことは、プログラマなら誰もが知っている。

確かに、プログラミングには理論が存在する。ソフトウェア工学と呼ばれているものがそれだ。プログラミングの外にいる人の目には、プログラミングとはソフトウェア工学の実践であって、練習によって習得できるものと映るかもしれない。しかし、そうだとしたら、どうしてこんなにも多くのソフトウェア開発プロジェクトが失敗するのだろう? プログラマはみんなあまりにも怠惰過ぎて、練習を怠っているということだろうか?

私が思うに、プログラミングは野球みたいなものだ。ソフトウェア工学は長嶋さんのホームラン理論に似ている。この理論は間違ってはいないが、本当にホームランを打とうとするなら、理論以外の何かが必要だ。そして、重要なことは、あの長嶋さんでさえ、毎回ホームランを打てたわけではないということだ。

交流会の後の懇親会で、私はデザイナの一人に、「エンジニアリングはスキルですか?」と聞かれた。私は、「ある部分はスキルでしょう。しかし、センスが必要な部分もたくさんありますよ」と答えた。この時は酔っ払っていたのでうまく説明できなかったが、私が言いたかったのは、つまるところエンジニアリングとはホームラン理論だということだ。

だから、デザイナは、自分たちがやっていることがスキルかセンスかなんて悩む必要はないと思う。たぶん、デザインもホームラン理論だ。部外者から見るとセンスに見えるかもしれないが、実際にはスキルとセンスの混合物なのだろう。

最後に、今回の交流会を主催し、そして私を呼んでくれたSetsucoCMSのみんなにお礼を言おう。彼らはとても若く、パワフルで、情熱に溢れている。そして何より、彼らはものづくりに対してとても真面目である。彼らの今後の活躍には私は大きく期待するし、また、今後もこういった交流会に呼んでほしいと思う。