2011-12-24

Fluxflex+Dozens+ムームードメインで、独自ドメインなWordPress製HPをサクッと作ってみた





突然ですが、沖縄の(変態)エンジニアが集う eXtreme HAGO というLT大会の公式HPを作りました!

eXtreme HAGO
http://xhago.net/

せっかくHP作ったので、沖縄の(禁則事項です)っぷりを、このブログで紹介したい...気持ちは山々なんですが、そういう紹介は公式HPに任せておいて、この記事では、そのサイトを作るまでにやったことについて話します。具体的には、WordPress + Fluxflex + Dozens + ムームードメインを使って、独自ドメインのWordPress製HPを作る過程について、サクッとまとめます。

注1:以下の"xhago" という文字列は、自分のプロジェクト名に適宜変換して下さい。
注2:各サービスの登録手続きなどは省力します。全部説明すると長いので。



0. 前提
- fluxflex / dozens / ムームードメインに Sign Upしてること(当然)
- fluxflexの Casual プラン以上の契約をしていること。(じゃないと独自ドメインが使えない)
- 1,000円ぐらい(ドメイン取得代)を持っていること。(世の中お金です)


1. fluxflex でプロジェクト("xhago")を作成します。



2. fluxflex の App Garage から、WordPress(jp) をポチって、"xhago"をターゲットにする。






3. ムームードメインから、独自ドメイン(例:"xhago.net")を購入。

xhago.net は既に僕が購入したので、登録出来ません。


4. 言われた通りに登録手続きを行い、「ドメイン取得」まで進む。


5. 「ドメイン取得」時の、ネームサーバを設定する画面で、次のように設定。他は適当に。


(上記は、世界に4つ点在するという伝説の Dozens ネームサーバです。四天王?


6. fluxflex から"xhago" -> Admin-> Domain にアクセスし、取得した独自ドメイン名を入力。



7. "Update" をクリックして、出て来た CNAME をメモります。

* "Verify Now"は、ここでは一旦無視します。


8. Dozens にログインして、コントロールパネルから独自ドメインを入力。



9. "xhago.net" -> "Add a record"に進み、次のように設定。"CONTENT"はさっきメモったヤツに置換して下さい。




10. fluxflex に戻り、さっき無視した"Verify Now"をクリックして、"Verified"となったら成功。

追記:TTLが7,200秒になっているため、場合によっては"failed"になることもあります。その場合は、1時間ほどおいてから再度お試し下さい。それでも"failed"になる場合は、もう一度設定した項目をご確認下さい。


11.最後に、取得した独自ドメインのURL("www.xhago.net")アクセスし、WordPressの画面が出て来たらOKです。


とまぁ、こんな感じでサクッと独自ドメインのWordPress製HPが作れます。世の中便利になりましたねー。
あとは、WordPressを使って、煮るなり焼くなり好きにして頂ければよいかと。

以上、ご参考になれば幸い。

2011-12-23

Xv6 Commentary の Scheduler の章をオレオレ翻訳してみた




最近よくお世話になっている下北沢OSSCafeで、なにやらSmooth CoffeeScriptの翻訳プロジェクトという、とても面白そうなプロジェクトが走っているみたいです。「すげぇ!僕も参加してぇ!」というわけで、その勢いに追従して(?)、1年前ぐらいに僕がオレオレ翻訳した xv6 commentary の Scheduler の章を公開します。ちなみにこの xv6 とは、教育用のOSで、MIT の強者どもが、OSの授業のためだけに作ったという伝説の一品です。ソースコードは約1万行ありますが、コメントやらなんやらが色々挿入されているので、実際のところは1万行未満です(な、なんだってーΩΩΩ)。

ただ、言い訳ですが、元々は誰かに見せるつもりではなかったので、翻訳は結構適当です。また、xv6 も毎年アップデートされてるみたいなので、可能であれば原本を見た方が良いと思います。とまぁ、そんな状態ではありますが、それでも参考になれば幸いです。ライセンスはMITライセンスだそうです。

Xv6 - Scheduler オレオレ翻訳, Google Docs
GitHub版 (勢いで repos も作ってみたでござる.)

上記のリンクはいずれも"Allow anyone to edit"な状態なので、一応、現状の Snapshot を以下にコピペしておきます。ご参考(になるのか?)まで。

追記:
さっきちょっと確認してみたところ、さっそく章構成やらなんやらが変わってました。5章ではなく4章になってて、タイトルも"Scheduling"になってます。しかも、僕が読んでたときよりもっと図解が多くなってる!ということで、やっぱし英語読める人とかは原本を読んで下さいね。

以下、オレオレ翻訳
=======================


Chapter 5

[Scheduling]

OSは、実際に持っているプロセッサの数よりも多くのプロセスを走らせているかのように見える。プロセス間でプロセッサを、タイムシェアするためには、いくつかの機構が必要である。一般的なアプローチは各プロセスに仮想プロセッサを割当て、OSが仮想プロセッサを多重化させて1つの物理プロセッサに割り当てることである。

Xv6は、複数のプロセスを並列に実行するための機構を持っている。もし二つの異なったプロセスが1つの物理CPUを競合しているとき、Xv6はその2つのプロセスを多重化する。そして、実行プロセスと実行待ちプロセスを、1秒間に複数回スイッチさせている。Xv6は多重化を用いて、各プロセスがそれぞれ1つのCPUを独占させているかのように振る舞っている。これは、Xv6がメモリアロケーターと HWセグメンテーションを用いて各プロセスにメモリを割り当てたときと似ている。

多重化を実装するためには、いくつかの課題を乗り越えなくては成らない。1つ目は、どうやってあるプロセスと、他のプロセスをスイッチさせるか、である。Xv6はコンテキストスイッチの標準的なメカニズムを使用している。そのメカニズムはシンプルであるが、OSの中では最も難解のコードである。2つ目は、どうやって透過的にコンテキストスイッチを行うか、である。Xv6はタイマ割り込みを用いてコンテキストスイッチを行うという、標準的なテクニックを用いている。3つ目は、プロセスが並列に走るかもしれない、ということである。これによって発生する競合には、ロック機構を用いて対処している。4つ目は、プロセスがその処理を終えたとき、そのプロセスは他のプロセスと共に多重化することができない、ということである。プロセスを消去することは容易ではない。プロセスは自分自身を消去できない。なぜなら、そのプロセスを消去するためには、そのプロセスを実行しなければならないからである。Xv6はこういった問題に対して、出来るだけ直接的な方法を用いて解決しようと試みている。しかしながら、結果的に出来上がったコードは、ややトリッキーである。

一度多重化されたプロセスが実行されると、Xv6はその実行プロセスと他のプロセスとを協調させなくてはならない。しばしば、あるプロセスは、他のプロセスの何らかのアクションの実行が終わるのを待つことがある。その処理が終わったかどうかを繰り返しチェックするために、CPUを無駄に浪費することは無駄である。従って、Xv6はプロセスにsleepという機構を持たせて、他プロセスのある処理が終わるまで眠らせることが出来る。また、他のプロセスは、眠っているプロセスを起こすことが出来る。ただし、プロセスは並列に実行されるため、眠っているプロセスを起こし忘れる危険性がある。そういった問題と解決策の例として
、本チャプターでは、pipeの実装についても触れている。


[Code: Scheduler]
2章では、ユーザスペースにおけるスケジューラについて俯瞰した。本章では、スケジューラのもっと深い部分について触れてみよう。各プロセスは、ブート時にmpmain()を走らせている。mpmain()の最後部では、scheduler()を呼び出している(1263)。

scheduler()は単純なループを実行している(1908)。つまり、(1)次に走らせるプロセスを探し、(2)そのプロセスが止まるまで実行し続ける、ことである。このループの最初部では、scheduler()はsti()を用いて明示的に割り込みを許可している(1914)。これにより、もしHW割込みが処理されるの待っているような場合でも、スケジューラのCPUは、HW割込みを先に処理出来るようになる。その後、スケジューラはプロセステーブルの中から実行可能なプロセスを探す。実行可能なプロセスとは、"p->state == RUNNABLE"となるプロセスのことである。スケジューラが実行可能なプロセスを発見すると、現在のプロセスを変数cpに格納する。その後、スケジューラはusegment()を用いてユーザセグメントを更新し、そのプロセスの状態をRUNNINGにセットする。最後に、swtch()を呼ぶことで、そのプロセスの実行を開始する(1922-1928)。


[Code: Context Switching]
Xv6の各プロセスはkernel stackとregister setを持っている(2章参照)。各CPUはkernel stackを持っていて、スケジューラを走らせるときにそのkernel stackを用いる。swtch()はスケジューラのコンテキスト情報(stack + register)を保存する。そして、現在のプロセスのコンテキスト情報と、次に実行するプロセスのコンテキスト情報とをスイッチする。プロセスがCPUを手放すとき、プロセスはswtch()を呼び出して自身のコンテキスト情報を保存し、returnを用いてスケジューラにコンテキスト情報を返す。各コンテキスト情報はstruct context *よって宣言されている。つまり、関係のある情報の構造体に対するポインタである。swtch()は2つの引数を取る。struct context **oldと、struct context *newである。swtch()は現在のコンテキスト情報を保存し、ポインタをoldコンテキストに格納する。そして、newによって与えられた新たなコンテキスト情報を復元する。

スケジューラのswtch()の振る舞いを追う代わりに、まずユーザプロセスがどのように復元されるかを追ってみよう。私たちは、3章で割り込み処理の末端でtrap()がyield()を呼び出す様子は理解した。yield()は、swtch()を用いて現在のコンテキスト情報をcp->contextに格納するsched関数を呼び出す。そして、スケジューラのコンテキストを情報を、以前保存されたc->contextの状態にスイッチさせる(1967)。

swtch()は、eaxレジスタとedxレジスタを読み込むことで開始する(2202, 2209-2210)。swtch()は、swtch()がスタックポインタを変更する前に、この操作を行わなければ成らない。そして、espレジスタを通して引数にアクセス出来ないようにする。そして、swtch()はレジスタの状態をプッシュし、現在のスタック上にコンテクスト情報の構造体を生成する。呼び出し元が保存するレジスタは、保存される必要がある。つまり、x86の慣習により、ebp,ebx,esi,ebp, そして, espレジスタを保存する必要がある。swtch()は最初の4つのレジスタを明示的にプッシュする(2213-2216)。struct context*は*oldに書かれているため(2219)、swtch()は暗黙的にespレジスタを保存する。また、さらにもう1つ重要なレジスタが存在する。それは、eipというプログラムカウンタである。eipはswtch()を呼び出した命令の場所が保存されている。そして、その命令の場所は、スタックに置ける、ebpレジスタの丁度真上にある。古いコンテキスト情報を保存したら、swtch()は新しいコンテキスト情報の復元の準備を行う。swtch()は、新しいコンテキスト情報を指すポインタを、スタックポインタに移動させる(2220)。新しいスタックは、swtch()が先ほど使っていた古いスタックと同じフォームを持っている。新しいスタックは、swtch()を呼び出す前までは、古いスタックであった。つまり、swtch()は、新しいコンテキスト情報を復元するために、シーケンスを逆転させることが出来る。swtch()はedi,esi,ebx,ebpレジスタの情報をポップし、そして、返り値として返す(2223-2227)。swtch()はスタックポインタを変更し終わったため、各レジスタの値は復元され、戻されたアドレスには新しいコンテキスト情報がある。

私たちの用いる例では、sched()は、c->contextとスイッチさせるためにswtch関数を呼び出した。c->contextとは、各CPUが持つスケジューラのコンテキスト情報である。その新しいコンテキスト情報は、scheduler()のswtch関数の呼び出しによって、保存される(1928)。私たちが追っているswtch関数がreturnされるとき、swtch関数は、sched()ではなく、scheduler関数にreturnされる。そして、スタックポインタは、initproc()のkernel stackではなく、スケジューラのスタックを指すことになる。


[Code: Scheduling]
最後のセクションとして、swtch()の低レベルの詳細を見てみよう。具体的には、swtch()の、プロセスからスケジューラまでの振る舞いと、スケジューラからプロセスに戻ってくるまでの振る舞いを見てみよう。Xv6の慣習として、CPUを手放したいプロセスは、必ずプロセステーブルのptable.lockに鍵をかけなくてはならない。また、取得したロックは必ず解放されなければならない。次に、自分自身の状態(cp->state)を必ず更新し、最後にsched関数を呼び出さなくてはならない。yield関数は、この慣習に従っている(1973)。つまり、sleep()とexit()を実行している。これらの関数は後で見ることにしよう。sched()は自分たちの状況と、その予想される結果に対して、二重のチェックを行っている。というのも、ロックが取得されてからは、CPUは、割り込み不許可の状態で動作しているからである。最後に、sched関数はswtch()を呼び出し、現在のコンテキスト情報をcp->contextに格納している。そして、スケジューラのコンテキスト情報をc->contextにスイッチさせている。swtch()は、スケジューラのスタック情報を返す。これは、スケジューラのswtch関数がスケジューラのスタック情報を返す状況に似ている。スケジューラはforループを繰り返し行い、実行可能状態のプロセスを探す。そして、スイッチを行う。スケジューラはこれらのサイクルを繰り返す。

私たちはXv6がswtch関数の呼び出しを跨いでptable.lockが取得されるのを見てきた。つまり、swtch関数を呼び出す関数は必ずロックを取得していなければならない。そして、ロックのコントロールはスイッチ後のコードを通る。この関数は、一般的なロック機構とは異なる。ロック機構の典型的な慣習は、ロックを取得したスレッドが、そのロックの解放する責任を持つことである。これにより、正しく動作していることを簡単に確認することが出来る。というのも、コンテキストスイッチは、典型的な慣習を破る必要がある。なぜなら、各プロセスの構造体において、ptable.lockは、stateとcontextのフィールドを保護する必要があるからである。ロック機構を用いない場合、コンテキストスイッチ中に、プロセスがyield関数を呼び出す可能性がある。つまり、プロセスの状態をRUNNABLEにして、CPUを手放すためのswtch()を呼び出す前に、異なるCPUがswtch関数を用いてそのプロセスを実行する危険性がある。他のCPUがswtch関数を呼び出すことで、古いコンテキスト情報を使う可能性がある。また、ロック機構を用いない状態では、2つのCPUが同じスタックを同時に使う危険性もある。そして、いずれの状態も正しくない。

この問題を回避するために、Xv6は、プロセッサを解放するスレッドがptable.lockを取得する、という慣習に従っている。そして、そのスレッドが、プロセッサが次に解放するロックを取得する。この慣習を簡潔にするため、スレッドは、sched関数の中で常にプロセッサを手放すようにしてる。これにより、もしあるスレッドが、Xv6がスレッド間をスイッチする部分の行をプリントアウトしている場合、次の簡単なパターンを観測することが出来るだろう(1928,1967,1928,1967などなど)。様式化されたスレッド間スイッチングが起こるプロシージャは、ときどき、対応する2つのco-routinesを参照している。例えば、sched()とscheduler()は、それぞれが互いに参照し合うco-routinesである。

新しいプロセスにスイッチするためのスケジューラのswtch()が、sched関数の中で終わらないケースが存在する。私たちは2章でそのケースを理解した。つまり、新しいプロセスが最初にスケジュールされたとき、そのプロセスはforkret()によって始まる(1984)。forkret()は、ptable.lockを解放を用いて、xv6の慣習に従うためだけのために存在している。それ以外の場合では、新しいプロセスはtrapret()によって始まる。


[Sleep and Wakeup]

ロックは、CPUとプロセスが互いに干渉することを回避するのに役立ちます。また、スケジューリングは、プロセスがCPUを共有するときに役立ちます。しかしながら、今までの内容には、プロセス間の通信を簡単にする抽象化機構はありません。Sleep/wakeup機構は、あるプロセスが何らかのイベントを待つために眠り、そのイベントが発生したら他プロセスがそのプロセスを起こすという機構を用いることで、その穴を埋めてくれます。

具体的な例を見るために、シンプルなproducer/consumerのキューを見てみましょう。そのキューは、あるプロセスが、他のプロセスに対して0でないポインタを送ることを許可します。もしたった1つのsenderとreceiverしかいなく、また、それぞれが別々のCPUで実行されている場合、次の実装は正しいと言えるでしょう。

struct q {
    void *ptr;
};

void* send(struct q *q, void *p){
    while(q->ptr != 0)
        ;
    q->ptr = p;
}

void* recv(struct q *q){
    void *p;

    while((p = p->ptr) == 0)
        ;
    q->ptr = 0;
    return p;
}

send()は、キューが空になるまで(ptr == 0になるまで)ループする。その後、send()はポインタpをキューの中に置く。recv()はキューが空でない間、ポインタpを外に取り出す。2つの異なったプロセスが走るとき、send()とrecv()は、両方ともq->ptrを編集する。しかしsend()は、ポインタが0であるときだけ、ポインタに書き込む。また、recv()は、ポインタが0でないときだけ、ポインタに書き込む。従って、send()とrecv()は相互にステップを踏むわけではない。

上述の実装は正しいかもしれないが、非常に高価である。もしsenderが時々send()する場合、receiverはポインタの値が変わるまでの間、常にwhileループでスピンし続けなくてはいけなくなるだろう。もしsend()がポインタへ到達したことをreceiverに対して通知する何らかの方法があったのだとしたら、receiverのCPUは、もっと生産的な仕事をすることが出来たかもしれない。sleep()とwakeup()は、そのような機構を提供する。sleep(chan)は、wait channelと呼ばれるポインタ"chan"の上でsleepする。chanは、参照されるためではなく、アドレスとして使われるため、chanはどんな種類のポインタであっても良い。sleep()は、呼び出し元のプロセスをsleepさせ、CPUを他の仕事のために解放する。sleep()は、プロセスが再び起きるまでreturnしない。wakeup(chan)は、chanの上で眠っているプロセスがあれば、各sleep()にreturnを実行させることで、chanの上で眠っている全てのプロセスを起こす。sleep()とwakeup()の機構を使うことで、上述の実装は次のように書き換えることが出来るだろう。

void* send(struct q *q, void *p){
    while(q->ptr != 0)
        ;
    q->ptr = p;
    wakeup(q);    /* wake recv */
}

void* recv(struct q *q){
    void *p;

    while((p = q->ptr) == 0)    /* (1) */
        sleep(q);               /* (2) */
    q->ptr = 0;
    return p;
}

このコードはより効率的であるが、もはや正しくはない。なぜなら、このコードは"lost wake up"問題を起こすからである。recv()が、(1)の箇所で"q->ptr == 0"であることが分かり、sleep()を呼ぼうとするとしよう。recv()がsleepする前は、send()は他のCPU上で走っている。つまり、send()がq->ptrを0で無い値に書き換え、wakeupを呼びだしたとき、どのプロセスの眠っていないことが分かる。このとき、recv()は(2)の箇所でsleep()を実行するだろう。recv()はsleep()を呼び、sleepする。このとき、"lost wake up"問題が発生する。つまり、recv()は、既に0以外の値に書き換えられているポインタに対して、sleep()をして待ち続けることになる。次のsend()は、recv()がキューの中のポインタが消費されることを期待して、sleepして待ち続けるだろう。このような現象を、システムがデッドロックしたという。

この問題の原因は、recv()がq->ptr==0のときにだけsleepするという不定状態が、send()が間違ったタイミングで走ることで、侵害されることによって発生される。この不定状態を保持するためには、ロック機構を導入し、呼び出し元プロセスが眠っているときのみsleep()はロックを解放できるようにすれば良い。一度呼び出し元プロセスが再び起こされたとき、sleep()はreturnの直前でロックを取得する。次のコードは正しく、また、recvがwaitしているときも、CPUを効率的に使うことが出来る。

struct q {
    struct spinlock lock;
    void *ptr;
};

void* send(struct q *q, void *p){
    lock(&q->lock);
    while(q->ptr != 0)
        ;
    q->ptr = p;
    wakeup(q);
    unlock(&q->lock);
}

void* recv(struct q *q){
    void *p;

    lock(&q->lock);
    while((p = q->ptr) == 0)
        sleep(q, &q->lock);
    q->ptr = 0;
    unlock(&q->lock);
    return p;
}

1つの完全な実装は、receiverが1つ前のsend()の値の消費を待っているようなsend()時においても、sleepできるようにすることだろう。



[Code: Sleep and Wakeup]

Xv6におけるsleep()/wakeup()の実装を見てみよう。基本的なアイデアは、sleep関数で、カレントプロセスにSLEEPINGとマークすることである。次に、sched関数を呼び出し、プロセッサを解放する。wakup()は与えられたポインタの上で眠っているプロセスを探し、発券したらそのプロセスにRUNNABLEとしてマークする。

sleep()は、いくつかの健全なチェックから始まる(2003)。そのチェックとは、カレントプロセスがあることと(2005-2006)、sleep()はロックを通過し終えたばかりであることである(2008-2009)。次に、sleep()はptable.lockを取得する(2018)。このとき、sleepしようとしているそのプロセスは、ptable.lockとlkを両方とも保持している。lkを保持することはcaller(e.g. recv())にとって必要なことであった。カレントプロセスは、他のどのプロセス(e.g. send())もwakeup(chan)を開始することが出来るだろう。このとき、sleep()はptable.lockを保持していて、sleep()は安全にlkを解放することが出来る。言い換えると、ある他のプロセスがwakeup(chan)を開始させたとき、wakeup()はptable.lockを取得できるまでの間、wakeup()が動くことは無い。よって、wakeup()は、sleep()を見失わないように、sleep()の実行が終了するまでの間、待たなければならない。

やや一般的ではない複雑性も存在する。もしlsと&ptable.lockが同じであるとき、sleep()は、&ptable.lockと同様の方法でlkを取得したり、lkと同様の方法で&ptable.lockを解放することによって、デッドロックを起こすかもしれない。この場合、sleep()はacquireとreleaseが互いにキャンセルしてしまったり、また、完全にスキップしてしまったりすることを考えなければならない。

sleep()がptable.lockを保持し、他のどのロックも取得していないとき、sleep()は、sleep channelを記録し、プロセスの状態を変更し、また、sched()を呼ぶことによって、プロセスをsleepさせることが出来る(2023-2025)。

いくつかのチェックポイントを過ぎると、プロセスはwakeup(chan)を呼び出す。wakeup()はptable.lockを取得し、実際の仕事を行うwakeup1関数を呼ぶ(2053)。wakeup()関すがptable.lockを保持するということは、wakeup()がプロセスの状態を操作するという点と、ptable.lockがsleep()とwakeup()が互いに見失い合わないという点から、重要なことだと言える(wakeup1()は分離された機能である。というのも、スケジューラは、既にptable.lockを取得しているとき、wakeup()が実行する必要があるからである。この例は後で見ることにしよう。)。wakeup1()はプロセステーブルを走査する(2053)。schedulerは、適切なchanを持つSLEEPING中のプロセスを発見したとき、schedulerはそのプロセスの状態をRUNNABLEに変更する。これにより、次にschedulerが動作されるとき、schedulerは、そのプロセスが実行可能であることが分かる。

※ 他にも、Spurious Wakeups(偽りのwakeups)という複雑な問題も存在する。




SCR輪講 後半
===========
[Code: Pipes]

本章の序盤で私たちが使ったシンプルなキューはオモチャであった。しかし、Xv6が実際に使っているキューでは、readersとwritersを同期させるために、sleep()とwakeup()を使用している。Xv6のキューとは、pipeの実装である。私たちはpipeのためのインタフェースを0章で見た。具体的には、pipeの末端に書かれるバイトは、kernel buffer内にコピーされ、その後、そのpipeの他端から読み出す事が出来る(FIFO Queue)。今後の章では、pipeの周りにあるファイルシステムのサポートについて触れていく。本章では、pipewrite()とpiperead()の実装について見てみよう。

各pipeはstruct pipeとして宣言される。struct pipeはlockとdataバッファを持っている。nreadとnwriteというフィールドは、バッファから読み出した/バッファに書き出したバイト数をカウントする。また、そのバッファはラップされている。つまり、buf[PIPESIZE-1]の直後に書かれている次のバイトはbuf[0]である。しかし、カウンタはラップされない。この慣習により、(nwrite == nread+PIPESIZE)という状態の満杯のバッファと、(nwrite == nread)という状態の空のバッファを見分けるときに役立つ。しかし、一方で、バッファのインデックスの際は、buf[nread]やnwriteの代わりに、buf[nread % PIPESIZE]を使わなければならない。なお、piperead関数やpipewrite関数は、別々のCPUによって同時に呼び出される可能性がある事に注意する必要がある。

pipewrite()はpipeのロックを取得する事から始まる(5230)。ロックを取得する事により、count,dataなどの変数を保護する事が出来る。このとき、例えばpiperead()も同様にしてロックを取得しようとしたとしても、ロックを取得する事は出来ない(5251)。この場合、piperead()はロックが取得出来るまでスピンし続けるだろう(1373)。piperead()が待っている間、pipewrite()は、交互にaddr[0], addr[1], ..., addr[n-1]を書き換える動作を繰り返す(5244)。このループの間、pipewrite()はバッファを満たしていく(5236)。バッファが満杯になった場合、pipewrite()はwakeup関数を呼び出し、sleepしているreaderに対してデータが読み込み待ちであることを伝える。そして、pipewrite()は&p->nwriteに対してsleepを行い、readerがbufferからデータを取り出すのを待つ。sleep()は、pipewrite()がsleepするプロセスの途中で、p->lockを解放する。

p->lockが取得可能なとき、piperead()はp->lockの取得を管理し、その後、pipeからデータを読み出す。具体的には、piperead()は(p->nread != p->nwrite)であることをチェックする(5256)。つまり、pipewrite()は、p->nwrite == p->nread+PIPESIZEのとき、sleepし始める(5236)。従って、piperead()は、forループを抜けると、pipeからデータをコピーし、コピーしたバイト数だけnreadの値をインクリメントする(5263-5267)。最初は、pipeに多くのバイトを書き込む事が可能である。よって、piperead()はwakeup関数を呼び出し、眠っているwriterを起こす。

wakeup関数は&p->nwriteに対して眠っているプロセスを見つけ、プロセスの状態をRUNNABLEに変更する。このとき、眠っているプロセスとは、pipewrite()を走らせていたが、bufferが満杯であるためにsleepしたプロセスのことである。

他CPU上のスケジューラが異なるプロセスを走らせようとしている場合、pipewrite()はすぐに走り始める事は無い。代わりに、piperead()は、piperead関数を再び呼んだcaller関数に戻る。初めてpiperead()がpipe内のバッファを全て読み出したときは、(p->nread == p->nwrite)という条件式が成立する。その後、piperead()は、&p->nread上でsleepし、データが書き出されるのを待つ(5261)。piperead()を呼び出しているプロセスがsleep状態のとき、sleep関数をreturnさせることで、CPUはpipewrite()のプロセスを走らせる(5242)。pipewrite()は、bufferに残ったデータをコピーした後、このループを終わらせる(5244)。新しいデータを待っているreaderが存在する場合、returnする直前で、pipewrite()をwakeup関数を呼ぶ。残されたpiperead()は走り続け、pipeから新しいデータをコピーする(pipe.c/piperead-sleep/)。


[Code: Wait and exit]
sleep()とwakeup()は、キューの実装時に必ず必要というわけでは無い。sleep()とwakeup()は、チェックしたいコードや待つ必要がある状況において、動作する。0章で見たように、親プロセスは、子プロセスがexitするのを、wait関数を呼ぶ事で待つ事が出来る。Xv6においては、子プロセスがexitしたとき、子プロセスはすぐに死ぬわけではない。代わりに、子プロセスはZOMBIEというプロセス状態にスイッチして待つ。親プロセスがwait関数を呼んで、子プロセスがexitしたことを知るまで、子プロセスはZOMBIE状態で待つ。親プロセスが子プロセスを回収したとき、子プロセスが所有しているメモリを解放する。そして、struct procが再利用出来るように準備する。各プロセスの構造体は、p->parentに親プロセスへのポインタを格納する。もし子プロセスがexitする前に親プロセスがexitしてしまった場合は、initプロセスが、子プロセスのexitに対応する。これは、親プロセスが、子プロセスが終了する前にexitしてしまったときのために必要なステップである。全てのプロセスの構造体はptable.lockによって保護されている。

wait関数は、ptable.lockを取得することから始まる。wait関数は、プロセステーブルを走査し、子プロセスを探す。wait関数は、まず、カレントプロセスに子プロセスが存在し、また、そのどれも未だexitしていないかどうかをチェックする。この場合、wait関数はsleep関数を呼び出し、子プロセスのいずれかがexitするのを待ち、ループする。ここで、sleep状態から解放されるlockはptable.lockである。上述したケースは、その一例である。

exit関数はptable.lockを取得し、その後、カレントプロセスの親プロセスを起こす(2126)。exit関数は未だカレントプロセスをZOMBIEプロセスに変更していないので、ロックの取得は未だ早過ぎるかもしれない。しかし、このロック取得は安全である。親プロセスはRUNNABLE状態に変更されるかもしれないが、wait関数にループは、exit関数がsched関数からスケジューラに入り、ptable.lockを解放するまでの間、走る事は出来ない。従って、プロセスの状態がZOMBIEになるまでの間、wait関数がexitするプロセスをチェックする事は無い(2138)。exit関数がリスケジュールする直前で、親プロセスは、子プロセスからinitプロセスまでの全てのプロセスをチェックする。最終的に、exit関数は、shed関数を呼び出す事でCPUを手放す。

スケジューラは、wait関数によってsleep状態に成っている、exitするプロセスの親プロセスを選ぶ事が出来る(2188)。sleep関数の呼び出しは、ptable.lockをreturnする。具体的には、wait関数はプロセステーブルを再び走査し、exitしたプロセス(state == ZOMBIE)を見つける。wait関数は、子プロセスのPIDを記録する。その後、その子プロセスに関わるメモリを解放し、struct procをクリアする(2168-2175)。

子プロセスは、exit関数の中で上述のクリア作業を行う。重要な事は、親プロセスが、親プロセス自身によってp->kstackを解放することである。子プロセスがexit関数を走らせるとき、子プロセスのスタックは、p->kstackとして確保されたメモリに置かれる。子プロセスのスタックは、子プロセスがshed関数のswtch関数を通して解放される。そして、子プロセスの状態を移行する。こうする理由は、(1)スケジューラの処理がスタック上で走っているからである。また、(2)xv6は、sched関数とscheduler関数を相互ルーチンとして管理するからである。xv6は、子プロセスから直接scheduler関数の処理を呼び出す事は出来ない。なぜなら、scheduler関数の処理は、スタック上で走るからである。また、そのスタックは、wait関数を呼んでいる親プロセスによって削除される可能性がある。


[Scheduling concerns] /* 下書き */

- Spurious wakeupsについて
- p->killedのチェックについて
- thundering herdについて


[Real world]

sleep関数とwakeup関数はシンプルで効率的な同期メソッドである。しかし、他にも同期をする方法はある。そのうちの1つは、本章の序盤で見た"missed wakeups"問題を避ける同期メソッドである。オリジナルのUnixカーネルのsleep関数は、割込を禁止する。この方法で同期をすることができる理由は、Unixは1つのCPU上で動くシステムであるからである。xv6はマルチプロセッサ上で動かす事を想定しているため、xv6は明示的なロック機構をsleep関数に導入している。FreeBSDのmsleep関数も同じアプローチを取っている。Plan 9のsleep関数は、callback関数を使っている。callback関数は、sleepする直前で取得する、スケジュール用のロックと共に動作する。具体的には、callback関数は、直前のsleepの状況をチェックするときに役に立つ。これにより、"missed wakeups"問題を避けることが出来る。Linuxカーネルのsleep関数は、waitチャネルの代わりに、明示的なプロセスキューを使っている。Linuxカーネルのプロセスキューは、内部用のロックを持っている。(一見、コードは十分でないように見える。実際にはどうなるだろうか?)

chan関数とマッチするプロセスのために、wakeup関数内で全体のプロセスリストを走査する事は、非効率的である。より良い解決策は、sleep関数やwakeup関数内のchan関数を、sleep状態のプロセスリストを持っているデータ構造体に置換することである。Plan 9のsleep関数とwakeup関数は、待ち合わせ箇所やRendezの構造体を使う。多くのスレッドライブラリは、変化可能な条件としてその構造体を参照する。つまり、sleep関数とwakeup関数の処理はwait関数やsignal関数によって呼び出される。これらのメカニズムは、同様の手法を共有する。具体的には、sleepの条件は、sleepしている間、アトミックに解放されたロックによって保護される。

セマフォは、異なった協調メカニズムである。セマフォは、2つのオペレーション(increment/decrement, up/down)を持ったint型の値である。セマフォは、いつでもインクリメントする事が可能である。しかし、セマフォの値がゼロを下回る事は許されない。よって、他プロセスによってそのセマフォがインクリメントされるまで、ゼロのセマフォをデクリメントはsleepする。そして、2つのオペレーションはキャンセルされる。int型の値は、典型的には、実際のカウンタの値(e.g. pipeバッファのバイト数, プロセスが持っているZOMBIEプロセスの数)と呼応する。抽象化の1つとして明示的なカウンタを使用する事によって、"missed wakeup"問題を避ける事が出来る。このため、wakeupの発生回数の明示的なカウンタが存在する。このカウンタは、条件変数による"spurious wakeup"問題や、"thundering herd"問題を避けることも出来る。


[Exercises] /* 下書き */

sleep関数は、デッドロックを避けるために、(lk != &ptable.lock)であることをチェックしなければならない(2017-2020)。sleep関数は次のコードを

if(lk != &ptable.lock){
    acquire(&ptable.lock);
    release(lk);
}

次のコードに置き換える事によって、特別な状況を無視する事が出来るだろう。

release(lk);
acquire(&ptable.lock);
    .P2

これによりsleepをbreakする。どうやって?

ほとんどのプロセスのcleanupは、exitまたはwaitによって処理される。
上述のケースはexitを使っている。
このとき、freeを使ってはならない。
    p->stack
これにより、プロセスは、exit関数はopenファイルの近くにいなければならなくなる。
何故?
答えはpipeと関係している。

xv6においてセマフォを実装すること。
ミューテックスを使っても良いが、sleep関数とwakeup関数は使わないこと。
xv6のsleep関数とwakeup関数をセマフォに置換する。
結果を比較せよ。

[Additional reading]

cox and mullender, semaphores.
pike et al, sleep and wakeup.









2011-12-16

コワーキングと沖縄とお仕事募集中




このブログを書くまでに至った経緯を簡単に説明しよう(キリッ。

  1. 最近、Advent Calendar っていうブログのリレーが今流行りらしい。なんか面白そう。
  2. Fluxflex Meetup で知り合った @mzdakr さんが Coworking 版のソレをやってるらしい。
  3. って「Coworking」って何ぞや?まぁいいか。それは置いといて...
  4. ポチッ(イベント参加登録ボタンを押した音)
  5. さて、とりあえず Coworking について調べるか...
  6. OSSCafePAX Coworking を訪問。
  7. うおー!Coworking 超面白い!
  8. でも沖縄では未だ Coworking 出来る場所が無いっぽい...
  9. じゃあとりあえず沖縄で広めるか!  (←今ココ)

...というわけで、今に至ります。ナニはともあれ、まずはブログを書かにゃナニも始まりません。そこで、この記事では、「Coworkingとは何ぞや?」という疑問に答えるべく、初歩的な内容を書いていきます。そして後半では、沖縄におけるCoworkingの現状や、僕がやっている事などについて記します。



で、Coworkingって何ぞ?

僕の乏しい参加経験から勝手に推測すると、Coworkingとは、大中小の組織の規模を問わず、仕事の種類を問わず、各々あるいはグループ毎に、場所を共有して仕事をすることのようです。Coworking Space(Coworkingをする場所)についても、最初のうちは「IT系の個人や小規模な組織のためだけのスペースなのかなー」と思っていたけど、どうやらそういうところではないみたい。実際、コクヨや富士ゼロックス、NECのようないわゆる大手のところでも、こういった Coworking Space を設けて、Coworking を推奨する動きを見せています。なので、個人的には「色々な方々が、色々な方法で、Coworking する」といった理解をしています。


で、どんな人が来るの?

まず、僕のようなフリーランスな人だけが利用するスペースではないことは確かです。グループで、誰かと一緒に仕事をしに来る人もいます。最近は、コワーキングスペースを利用するため、オフィスを引き払った会社もあるようなので、今後は、より色んな形でコワーキングスペースを利用する人達が増えてくるのかもしれません。例えば、学生が勉強しに来るとか、子連れで仕事しに来るとか、etc.。いずれにせよ、僕は多様性のある環境が結構好きなので、こういった動きは、僕個人としては歓迎です。


で、どんな仕事してるの?

Coworking する人も色々いますが、彼ら/彼女らの仕事の種類も色々あります。一見、IT系の人向けの特殊な共有スペースかと思いきや、全くそうじゃないです。プログラマやデザイナだけでなく、コピーライター、ジャーナリストの方々もいます。

例えば、僕が OSSCafe を訪問していたとき、「スクリプトを書きに来ました」という方々と会いました。「なるほどやっぱしIT系の人が来るのね」と僕は思ったのですが、ところがどっこい彼らは全然 Lisp とか Python とか Ruby とか全然書いてない!「おいおいおいどういうことだよ...」って思ったら、コピーライターの業界では、どうやらキャッチコピーを創ることを「スクリプトを書く」と表現するみたいです。長くIT業界に浸かっていると、こういう他分野の表現に出会わないので、刺激的ですね。人も色々、仕事も色々、って感じ。


余談

PAX Coworking にいるときに、こんな会話を聞きました。

1. トゥルルルルル...(電話が鳴る)

2. 「もしもし、社長とお話したいのですが、社長はいらっしゃいますか?」

3. 「えーっと、どこの社長でしょうか?」

4. 「... ... ...」

当然ですけど、Coworking Space には、いわゆる社長な方が結構います。1人とは限りません。こういう、従来の働き方ではありえないような展開と巡り会える環境というのも、Coworking の面白い点の1つではないでしょうか。



ここまでのまとめ

僕はまだまだ駆け出しのフリーランスで、仕事もほとんど沖縄でしか取れていません。しかし、こういった Coworking Space の環境を使わせて頂くことで、能力と実績さえあれば、ネットワーキングがより簡単に出来ると考えています。フリーランスとしては非常に心地良い環境が構築されつつあると感じるので、個人的には嬉しいかぎりです。ときどき「もっと安くならないかなー」とか考えたりしますが、つまりは僕がもっと稼げばいいということなのでしょう。


"""
↑ここまで東京の話

↓ここから沖縄の話
"""


沖縄の現状

さて、じゃあ沖縄の現状はどうでしょうか? Coworking するスペースが既にあるかと聞かれると、僕の知る限り、今のところ沖縄には Coworking Space にあたる場所はほとんど無いです。ただ、僕は沖縄に対してヨーロッパに似た雰囲気を感じているので、もし沖縄に Coworking Space があったら、沖縄で「遊び半分、仕事半分」な Work Life Balance が気軽に体験出来て、非常に面白そうな気はしています。

僕は友達もコネも全くゼロの状態から始めたので、沖縄でフリーランスとして活動出来るようになるまでに、少々時間がかかってしまいました。しかし、もし Coworking Space が沖縄に既に出来ていたら、より簡単に、より早く沖縄でフリーランスとして活動出来ていたかもしれません。もしそんな環境が構築出来たら、ちょっとワクワクするでしょう?


なぜ沖縄?

ところで、この記事を読んでいる方の中には、もしかしたら「何で沖縄で活動する必要があるの?」って感じる方がいるかもしれません。そう思った方は、是非 @sayobs さんが書いた記事「沖縄に1ヶ月生活 – ノマドしてきた感想とか」を読んでみて下さい。きっと沖縄で「遊び半分、仕事半分」な Work Life Balance を体験したくなると思います。沖縄に Coworking Space 、あるいはそれに準ずる環境が出来た際には、皆さんも是非沖縄に来てみて下さい。


話を戻して...

現状で、Coworking Space に類するプロジェクトが1つも無い訳ではないです。名前こそ Coworking ではありませんが、それっぽいところ、それっぽい活動はちらほら見受けます。ここでは、その一部をご紹介しようと思います。


1. SHDH Okinawa

例えば、僕もちょっとお手伝いしている活動ですが、SuperHappyDevHouse Okinawa という活動があります。コンセプト自体は Coworking に非常に近いと思います。しかも無料!ただ、開催が月に一回なので、どちらかというと、勉強会と Coworking の中間ぐらいに位置する活動なのかなーと僕は思います。あと、開催地が沖縄の中部にあるので、車が無いとアクセスがつらいのも、やや難しい点です。


2. ギークハウス沖縄

他にもあります。ギークハウス沖縄というプロジェクトです。週末ものづくり講座の受講生が一枚噛んでいるので、僕もちょくちょく現況を聞いています。ただ、こちらはまだオープンしていないので、今後どういった形態で活動が行われていくのかは、まだまだ分かりません。しかし、非常に面白い試みだと思うので、今後に期待です。


3. 名称不明なプロジェクト

で、最後に、僕のプロジェクト。名前は未だありません(Online Study Shared Space? OS3? )。とにかく、出来立てホヤホヤ。ITカレッジ沖縄という学校の教室が、月・木の夕方と、土曜日を終日借りれそうなので、「そこで何か面白いこと色々やろうぜ」的なプロジェクトです。早ければ来週ぐらいから始まるかもしれないし、永遠に始まらないかもしれません。とりあえず決定している事は、「電源や無線が使える場所を無料で提供してもらえる」ということだけです。

当初は Coworking Space っぽさをバリバリ出そうかなーと思ったのですが、無料で場所を提供して頂く関係で、「(学内外を問わず)学生に何らかのメリットがあること」が条件になります。あと、オープンソースであること。加えて、僕がアッチいったりコッチいったりするので、僕が常駐する事も出来ません。そこで、僕の辿り着いた(とりあえずの、暫定的な)結論は、次のようなスペースを創ることです。


そんな感じのスペースを創ろうかと考えています。Coworking というよりは、Costudying に近いような感じですかね。ただ、実施されるその瞬間までは、僕の脳内妄想に違いありません。なので、実施されるその日までは、生暖かい目で見守って下さい。



最後に

Coworking Advent CalendarのATNDには、次のような文章が書かれています。
10分で考えて5分で書く、気軽な内容でOKです。
全然ムリです。軽く10倍以上の時間がかかってます。来年はもっとサクサク書きたいですね。



ところで、来年の1月末から数ヶ月間シリコンバレーに滞在する予定なのですが、
その資金として、お仕事を絶賛募集中です。リモートでも出来る仕事だと嬉しいです。
仕事とは関係なくても、何か面白そうな話があれば yasulab@gmail.com までご連絡頂けると嬉しいです。
僕の最近の活動記録は http://www.yasulab.com/に書いてあります。
ではでは、皆さんからのお便りをお待ちしております。








2011-12-04

Make: Tokyo Meeting 07 に出展してきました!




「ブログを書くまでが勉強会です」ということで、早速レポート。

Make: Tokyo Meeting 07 に出展してきました!前日に沖縄で終日の講義があったので、若干弾丸ツアー的な部分もありましたが、なんとか間に合いました。

僕が出展した内容は、「週末ものづくり講座」というプロジェクトの紹介です。このプロジェクトは、「とにかく早く作って早く公開する」をモットーに、色んな物を作るプロジェクトです。具体的には、
仕事が終わった後の金曜夜から日曜の終わりまでに, アイデアからプロトタイプまでを一気に作成し, 月曜日にドヤ顔で「こんなん作っちゃったんだぜ(キリッ」と周りの人に発表する.
 といった感じで、毎週末活動をします。毎週1作品作るのが目標。

元々(というか元凶)は、ホイッスルアプリ3ヶ月で5万ダウンロードされたことです。小さい成功ではあるけれど、この結果から「サクッと作れるシンプルなモノ(*1)でも、皆が使ってくれるモノは、まだまだ結構あるんじゃね?」と考えるようになりました。

*1 ちなみに、ホイッスル on Androidの中身は30行。



「とりあえず実験してみよう」ってことで、勢いに任せて色々創ってみました。例えば、Simple TimekeeperとかNico Rank Inverterとか。結果としてうまくいったのはほんの少しで、残りのほとんどは失敗でしたが、しかし、やってみるとこれが案外面白い。

面白いので、皆にこの面白さを是非共有したい。IaaSとかPaaSとか、最近はモノを作って公開するための環境が非常によく整って来ているので、もっとアプリ作る人が増えてもいいと思うしね。なので、「じゃあ講座でも開くか」と考え、紆余曲折(告知とか交渉とか)を経て、週末ものづくり講座のカリキュラム(ver. 0.01)を完成させました。これが9月の話。で、10月からITカレッジ沖縄という学校で、実験的な授業がスタート。そして、11月の末に、無事1期生の授業が終わる(←今ココ)。といった感じです。

ちなみに、1期生の作品はコチラで順次公開していく予定です。また、ハッカソンというイベントで、1期生の1人とコラボって作った作品がコチラ



こんな感じの、わりと勢いと衝動任せのプロジェクトですが、反応は中々良いみたい。社交辞令かもしれないけど、例えば、こんな感じの反応を頂いたり。

最近は、レキサスアカデミーという沖縄のアプリ開発塾みたいなところで、専属講師としてものづくりのアレコレを教えています。定員は15名なんですが、既に満員間近。聴講生(正式な受講生ではないが、興味があるので参加する人)も来るぐらいので、個人的には嬉しい限り。

とまぁ、こんな感じの近況報告を、WIZDOM枠を使わせて頂き、Make: Tokyo Meeting 07で発表させて頂きました。こっちでの反応もいい感じでした。特に、「是非受講したい!」という話を貰えたのは非常に嬉しかったです。今は沖縄を拠点してやっているので、残念ながら沖縄以外では気軽に受講できないけれど、「誰か東京でやってくれないかなー」と悶々とする妄想する今日この頃。



最後に、宣伝ですが、WIZDOMの他のプロジェクトも非常に面白いです。例えば、GPL HouseとかCAPBOTとか。何やらハードウェア系がもっさりある感じなので、ここは是非彼ら/彼女らと情報交換したいなーとか、妄想に耽っています。

# 最近は妄想が酷い...


今年立ち上げた Okinawa.rb でも「クラウドからハンダ付けまで」をモットーに(僕が勝手に)掲げているので、ハード系な人達ともっと交流しなきゃあかんなー、と考えています。










2011-12-01

12月3日(土)にIT津梁パーク@沖縄で行われるイベントまとめ




各勉強会のメーリングリストにも流しましたが、ブログにも掲載します。
興味ある方は是非に。
------------------------------


こんにちは、@yasulab です。

今週土曜日(12月3日)は、IT津梁パークで次のイベントが行われます。
ご都合の着く方は是非是非ご参加を。

10:00-12:00 「アマゾン ウェブ サービスがもたらすITのニューワールド」

13:00-17:00 Okinawa.rbワークショップ(*1) in Okinawa SHDH (*2)

なお、15:00-16:00は、@libkinjo さんによる次の発表があります。

>■SHDH2 でもくもくしたアプリ dont-forget-something の解説
>・見所: accepts_nested_attributes_for, haml, やや力技 jquery などw
>■ロレーヌでもくもくしていた twitter 擬の解説
>・見所: rails 3.1/CoffeeScript/mongo/rspec/spork/factory_girl

モクモクと勉強/開発する際のBGMとしてどうぞ、とのことです。



また、13:00-17:00は、同じ会場で Lexues Academy(*3) が行われます。
週末ものづくり講座(*4)に関するアレコレを僕が話しているはずなので、
興味ある方は覗きに来て下さいね。今回は誰でも聴講することが可能です。
※追記:いつでも誰でも聴講することが可能です。

ではでは、参加する方は是非当日会場でお会いしましょう。

yasulab


*1 Okinawa.rb ワークショップってなんぞや?って方はこちらへどうぞ。

*2 Okinawa SHDHってナニ?という方はこちらへ。

*3 Lexues Academy

*4 週末ものづくり講座