<?xml version="1.0" encoding="UTF-8"?>

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns="http://purl.org/rss/1.0/"
>

<channel rdf:about="http://smpl.seesaa.net/">
<title>微酔半壊</title>
<link>http://smpl.seesaa.net/</link>
<description>&amp;nbsp;</description>
<dc:language>ja</dc:language>
<admin:generatorAgent rdf:resource="http://blog.seesaa.jp/" />
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://smpl.seesaa.net/article/72182431.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/71425025.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/46185956.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/37757606.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/37446952.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/36160135.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/29800843.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/25482628.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/24918184.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/19591451.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/16251405.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/9867276.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/9655835.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/9435409.html" />
<rdf:li rdf:resource="http://smpl.seesaa.net/article/9342186.html" />
</rdf:Seq>
</items>
</channel>

<item rdf:about="http://smpl.seesaa.net/article/72182431.html">
<link>http://smpl.seesaa.net/article/72182431.html</link>
<title>memcached</title>
<description>※本記事は当初HTMLに整形せずに掲載してしまい、ご覧になった方にはご不便をおかけしました。お詫び申し上げます。consistent hashingの記事でmemcachedが名前だけ出てきたので少し調べてみた。memcachedは単純なキーと値のペアによる分散キャッシュサーバ。高速で、非常に台数にスケールすることが利点という。ほとんど設定いらずでインストールしてそのまま起動できる。動的に生成するウェブページのキャッシュ等に用いられる。キャッシュの他HTTPのセッション情報の...</description>
<dc:subject>雑記</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2007-12-11T06:07:54+09:00</dc:date>
<content:encoded><![CDATA[
<!-- *sblog-encoded markdown/euc-jp+gzip+b64*
H4sIAIsJXkcAA0VSUW/SUBR+36/wcXuhuriY+Av2oIkPvC17IIwMksGMNMG/0/JdXBAGOLCUQtvR
0XbtgC2aqXNC0jkdYwkqZtMHE0/rgg/33pNzzne+75xzxS33tFZUrtHvXLdGy+HHj+DqZWkXChpw
JatxCgkmnQEgiKh2b+DCgQHN+UNWH9X9t857/EAeL1EJ8iRoQhb5fhdvtBYk9RyvyCsL2bm5lehm
Kp1I87EUfyceSccTqXX0fPrVeS7yjE9EN2Lcg3v3F5fuLi6F4nxyYwFWMpaMRqLx2BoKB+puGToq
rUOSVISGHiy1BGmvig/kGfrERDJDrM7Hef7pQ47LZDKhtUhqPRKKbia5WZxbQN+otM7gsKLQhG10
0WMnTKTGLjB2CsprVmRnzGASO2c1ockOhazstbdhCaI9VSdw21+0n3CZzCoUHUPGGDuwUej+6uj0
AjkaxUeyptjXh3uX5BnRYC0GNiWUHaD8AbeolwEGtZJZhkW9jf1OzHInT+uo69t+ZZZjL9gxOyFM
nXTOlHV+w+0eBZWvfNz/CHptdTkcfkLZSuD5yqbqN2dKXBfIwVENbN0u16DXM94daMRo0YLxL8/5
pNCaFRsTv3bQmwfRZ6kLjZtA+ZD8tvzd9AjnJRPPE/hM38KEQ7osuYjSLdokheOAVxeyO2ZzUtfr
V6SsxEY0DZHmYQfWMd2X8NoafbjsX2bqhlCfAgAA
-->
<p>※本記事は当初HTMLに整形せずに掲載してしまい、ご覧になった方にはご不便をおかけしました。お詫び申し上げます。</p>

<p><a href="/article/71425025.html">consistent hashingの記事</a>でmemcachedが名前だけ出てきたので少し調べてみた。</p>

<p><a href="http://www.danga.com/memcached/">memcached</a>は単純なキーと値のペアによる分散キャッシュサーバ。高速で、非常に台数にスケールすることが利点という。ほとんど設定いらずでインストールしてそのまま起動できる。</p>

<p>動的に生成するウェブページのキャッシュ等に用いられる。キャッシュの他HTTPのセッション情報のような消えてしまっても致命的でない情報の保持に使われることもある。検索してみると国内でもmixiやはてな等で広く使われているようだ。各種言語のクライアントライブラリも多い。</p>
<a name="more"></a><!-- *sblog-encoded markdown/euc-jp+gzip+b64*
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
g6HhoHr1kf8BZ7T70NZcAAA=
-->
<h4>目次</h4>

<ul>
<li><a href="#memcached_libevent">libevent</a></li>
<li><a href="#memcached_c10kproblem">クライアント1万台問題</a></li>
<li><a href="#memcached_memcached">memcached</a></li>
<li><a href="#memcached_RubyMemCache">Ruby-MemCache</a></li>
<li><a href="#memcached_libketama">libketama</a></li>
</ul>

<h4><span id="memcached_libevent">libevent</span></h4>

<p>memcachedは<a href="http://www.monkey.org/~provos/libevent/">libevent</a>というフレームワークを利用して書かれている。
libeventはネットワークサーバ用のイベント駆動型フレームワーク。後述の<a href="#memcached_c10kproblem">クライアント1万台問題</a>も考慮しノンブロッキングI/Oを駆使する高速なネットワークソフトウェアが書けるようになっている。
一般的な<em>select</em>や<em>poll</em>の他、<em>kqueue</em> (*BSD)、<em>epoll</em> (Linux)、<code>/dev/poll</code> (Solaris)といったOS固有のイベント通知機構にも対応している。</p>

<p>libevent利用の基本的な流れは次のようになる。<em>kqueue</em>等とよく似ている。</p>

<ol>
<li><code>event_init()</code>を呼び初期化</li>
<li>捕捉したいイベントを登録
<ol>
<li><code>event_set()</code>で監視するファイル記述子(ノンブロッキングモード)や条件、イベントハンドラの情報を構造体にセット</li>
<li><code>event_add()</code>で登録</li>
</ol></li>
<li><code>event_dispatch()</code>でイベントループに入る</li>
</ol>

<p>libeventのサイトにある<a href="http://www.monkey.org/~provos/libevent/event-test.c">event-test.c</a>がごく簡単なサンプルプログラム。そのWindows用コードを取り除いて簡潔にしたものを引用しておく。</p>

<pre><code>/*
 * Compile with:
 * cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent
 */

#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;sys/queue.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/time.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;errno.h&gt;

#include &lt;event.h&gt;

void
fifo_read(int fd, short event, void *arg)
{
        char buf[255];
        int len;
        struct event *ev = arg;

        /* Reschedule this event */
        event_add(ev, NULL);

        fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n",
                fd, event, arg);
        len = read(fd, buf, sizeof(buf) - 1);

        if (len == -1) {
                perror("read");
                return;
        } else if (len == 0) {
                fprintf(stderr, "Connection closed\n");
                return;
        }

        buf[len] = '\0';
        fprintf(stdout, "Read: %s\n", buf);
}

int
main (int argc, char **argv)
{
        struct event evfifo;
        struct stat st;
        char *fifo = "event.fifo";
        int socket;

        if (lstat (fifo, &amp;st) == 0) {
                if ((st.st_mode &amp; S_IFMT) == S_IFREG) {
                        errno = EEXIST;
                        perror("lstat");
                        exit (1);
                }
        }

        unlink (fifo);
        if (mkfifo (fifo, 0600) == -1) {
                perror("mkfifo");
                exit (1);
        }

        /* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */
#ifdef __linux
        socket = open (fifo, O_RDWR | O_NONBLOCK, 0);
#else
        socket = open (fifo, O_RDONLY | O_NONBLOCK, 0);
#endif

        if (socket == -1) {
                perror("open");
                exit (1);
        }

        fprintf(stderr, "Write data to %s\n", fifo);

        /* Initalize the event library */
        event_init();

        /* Initalize one event */
        event_set(&amp;evfifo, socket, EV_READ, fifo_read, &amp;evfifo);

        /* Add it to the active events, without a timeout */
        event_add(&amp;evfifo, NULL);

        event_dispatch();

        return (0);
}
</code></pre>

<h4><span id="memcached_c10kproblem">クライアント1万台問題</span></h4>

<p>クライアント(Client)と1万(10K)から「C10K problem」と書かれる。
十分強力なハードウェアが安価に得られる現在、マシンパワーだけなら数多のクライアントも恐くはなくなったが、同時に無数のクライアントを捌かねばならない時代には別の要因がボトルネックとして浮上しているというもの。</p>

<p>これを日本語で紹介した@ITの記事「<cite><a href="http://www.atmarkit.co.jp/news/analysis/200701/09/c10k.html">web2.0の先にあるC10K問題</a></cite>」ではOSのプロセス番号の最大数等からくる問題とされているが、元ネタの<cite><a href="http://www.kegel.com/c10k.html">The C10K problem</a></cite>の問題意識の中心はそれとは異なる。</p>

<h5>クライアントごとにスレッドを使うモデルは通用しない</h5>

<p>OSのマルチスレッドが一般化してからの一頃は1クライアントに1スレッドを割り当てるサーバも多く書かれたが、C10Kの状況ではこのモデルは破綻する。大抵のOSは少なくとも以前はそのような非常に多数のスレッドを扱う状況をあまり想定していなかったことや、スレッドの数だけスタックフレーム(例えば2MB)も必要なためにプロセスのメモリ空間の大きさからスレッド数も制約されてしまう(例えば512個)という理由による。</p>

<p>スレッドの代わりにクライアントごとにプロセスを割り当てる方式だとそれこそ@ITのプロセス数の制約の議論になる。</p>

<h5>一スレッドで多数のクライアントを同時に処理</h5>

<p>必然的に一つのスレッド(またはプロセス)で多数のクライアントを同時に処理する必要があり、<cite><a href="http://www.kegel.com/c10k.html">The C10K problem</a></cite>では</p>

<ul>
<li>ノンブロッキングI/Oと状態変化通知機構を利用する</li>
<li>非同期I/Oを使う</li>
</ul>

<p>この2つに特に焦点を当てている。</p>

<h5>状態変化通知</h5>

<p>状態変化通知(readiness change notification)とは、データが到着して入力が可能になった場合、または先の出力が完了してバッファが空き、次の出力が可能になった場合の変化をユーザプロセスに通知する方式。*BSDの<em>kqueue</em>、Linuxの<em>epoll</em>等がこれに当たる。</p>

<p>従来からUNIXで使われてきた<em>select</em>や<em>poll</em>は、プログラムが調査するたびにそのとき読み込み可能・出力可能なファイル記述子を報告するもの。<em>select</em>や<em>poll</em>の大きな問題として、<em>select</em>には調査するファイル記述子の数が<code>FD_SETSIZE</code>に制約されることが挙げられる。<em>poll</em>にはそのような制約はないものの、C10K環境では毎回長大なファイル記述子のリストを全てスキャンすることになり、多くの場合ほとんどのファイル記述子は準備状態にないだろうことから非常に無駄が多い。</p>

<p>Solarisの<code>/dev/poll</code>は<em>poll</em>を代替するもので、監視するファイル記述子のリストを一回だけ登録し、毎回渡す必要をなくしてパフォーマンスを改善したもの。</p>

<h5>ノンブロッキングI/O</h5>

<p>ノンブロッキングI/Oは文字通りブロックしない入出力方法。通常の入出力では指定したデータの出力が完了するまで、または指定したバイト数のデータを読み込むまでシステムコールから復帰せず、その間プロセスの実行は停止する。ノンブロッキングI/Oはそのとき到着していたデータだけを読み込み、またはそのときカーネルのバッファに書き込める分量だけを出力して即座に復帰する。1バイトも処理できない場合にはブロックしないでエラー(EWOULDBLOCK)を返す。</p>

<p>この場合にボトルネックはどこからくるかというと、ディスクアクセスで発生する(ディスクアクセスに対してノンブロッキングモードの指定は無効)。<em>mmap</em>されたファイルへのアクセスでも、<em>sendfile</em>でも同様。とにかく読み込むデータがメモリに載っていたかったが最後、ブロックは生じてしまう、すなわち無数のクライアントの処理が停止する。解決策は非同期I/Oを用いるかワーカースレッドに処理を任せてさっさと次のクライアントの処理に回ること。なおFreeBSDには<em>sendfile</em>にブロックを避けるオプションがある。</p>

<h5>非同期I/O</h5>

<p>非同期I/Oは<em>aio_*</em>(<em>aio_read</em>, <em>aio_write</em>等)というAPIで提供されているが、比較的新しいためあまり普及していないらしい。</p>

<p>非同期I/Oではデータの読み込み・書き出しの指示だけをカーネルに与えると即座に復帰する。カーネルが入出力を完了すると、ユーザプロセスはシグナルによってその通知を受け取る。</p>

<p>O'Reillyの本<cite><a href="http://www.amazon.co.jp/o/ASIN/1565920740/bisui-1-22/ref=nosim">POSIX.4: Programming for the Real World</a></cite>に非同期I/Oのよい紹介があるとされている。</p>

<p>WindowsにもI/O Completion Portという非同期I/Oと完了通知の枠組みがあるという。</p>

<h5>総じて</h5>

<p><cite><a href="http://www.kegel.com/c10k.html">The C10K problem</a></cite>では、いかに不要なブロッキングが発生してしまうのを回避し、マシンパワーにスケールした性能を出せるプログラムを構成するかが命題となっている。</p>

<p>読んでいるうちに「<a href="http://www.amazon.co.jp/o/ASIN/4478420408/bisui-1-22/ref=nosim">ザ・ゴール</a>」(制約条件の理論を題材にした小説。面白い)を思い出すような話だった。</p>

<h4><span id="memcached_memcached">memcachedの概要</span></h4>

<p>話をmemcachedに戻す。</p>

<p>memcachedは次のように起動する。オプションには<em>listen</em>するアドレス、ポート番号、使用するメモリサイズを指定する。</p>

<pre><code># ./memcached -d -m 2048 -l 10.0.0.40 -p 11211
</code></pre>

<p>クライアント1万台問題でも出てきたように、ディスクへのアクセスが入るとパフォーマンスが下がるのでオンメモリを保つことが重要なポイントとなる。そのため<em>mlockall</em>を用いて物理ページを占有するオプションが用意されている。</p>

<p>基本的な使い方は次のようなものになる。</p>

<ol>
<li>データベースへ問い合わせる前にキャッシュに載っていないかを調べる。載っていればそれを返す。</li>
<li>載っていない場合はデータベースに問い合わせる。得られた結果はキャッシュに登録する。</li>
</ol>

<p>キーは250文字までの空白を含まないテキスト、値は最大1MBまでの任意のデータ。</p>

<p>memcachedプロトコルには次のような操作が用意されている。</p>

<ul>
<li>set: オブジェクトを登録</li>
<li>add: 未登録の場合のみオブジェクトを登録</li>
<li>replace: 登録済の場合のみオブジェクトを変更</li>
<li>append, prepend: オブジェクトに追加</li>
<li>cas: 以前アクセスした時点から変更されていない場合のみオブジェクトを変更</li>
<li>incr, decr: オブジェクトを64ビット整数として更新</li>
<li>get: オブジェクトを得る</li>
<li>delete: オブジェクトを抹消</li>
<li>flush_all: 全て抹消</li>
<li>stats: キャッシュサーバの状態を報告</li>
<li>quit: キャッシュサーバを終了</li>
</ul>

<h4>consistent hashingとの関連は?</h4>

<p>memcachedそのもの(や、クライアントのほとんど)は全く関係ない。</p>

<p>memcachedそのものはキャッシュサーバ単体としての責任のみを担当していて、キャッシュサーバの選択方法、どれかのキャッシュサーバが落ちたときの対応等の一切はクライアントライブラリ任せとなっている。</p>

<p>そして多くのクライアントはconsistent hashingは実装していないため、FAQにもキャッシュサーバの追加や削除は全てのキャッシュを無効にするのと同じだという警告がある。</p>

<p>consistent hashingを利用した例としてlibketamaというクライアントライブラリが存在する(<a href="#memcached_libketama">後述</a>)。</p>

<h4>memcachedのソースコード</h4>

<p>ソースコードは小さく、*.cファイルだけだと7つ、5000行余りしかない。中心的なモジュールは次の5つ。</p>

<ul>
<li>assoc.c: キーからオブジェクト(厳密には次の節で述べるキャッシュアイテム)へマップするハッシュテーブル。</li>
<li>slab.c: キャッシュアイテムに用いるためのメモリ管理。キャッシュアイテムの大きさを十数段階に分け、階級ごとにメモリプールを作る。それぞれのメモリプールには必要に応じて1MBのメモリブロック単位(slab)でメモリを追加する。メモリプールは一定長のアイテムに分割され、要求があると大きさに応じた階級のメモリプールから空いているアイテムを返す。</li>
<li>items.c: キャッシュアイテムのモジュール。</li>
<li>memcached.c: メイン関数、ネットワークサーバとしての接続処理。</li>
<li>stats.c: 統計情報。</li>
</ul>

<h5>items.c</h5>

<p>キャッシュアイテムは次のような形式で一括してメモリ上に作られる。</p>

<pre><code>+------------------------------------+
| ヘッダ                             |
+------------------------------------+
| キー(NULL終端文字列) (+パディング) |
+------------------------------------+
| サフィクス                         |
+------------------------------------+
| データ                             |
+------------------------------------+
</code></pre>

<p>サフィクスはデータの長さやフラグを示す1行の文字列で、memcachedがクライアントの要求に対してデータ本体の前に通知するプレフィクスそのものになっている。</p>

<p>このアイテムは2つのデータ構造の要素になる。その一つはassoc.cによるハッシュテーブル、もう一つはitems.c内のリスト。items.cではメモリブロックの大きさ別にアイテムのリストを作り、キャッシュ管理(LRUアルゴリズムによるエクスパイア)を行っている。</p>

<p>またヘッダにはカウンタを持ち、参照カウント方式で管理もしている。</p>

<h5>memcached.c</h5>

<p>ネットワークサーバとしての本体部分。その大きさは約2700行と全体の半分を占める。</p>

<p>メイン関数は各種初期化をしlistenするソケットを作った後、libeventのイベントループに突入する。</p>

<h5>conn (構造体)</h5>

<p>次の構造体がヘッダmemcached.hで定義されている。
ノンブロッキングI/Oのためのバッファ以外にも要素は盛り込まれており、これを見るだけでも接続の処理は有限状態機械になっていることが感じられる。</p>

<pre><code>typedef struct {
    int    sfd;
    int    state;
    struct event event;
    short  ev_flags;
    short  which;   /* which events were just triggered */

    char   *rbuf;   /* buffer to read commands into */
    char   *rcurr;  /* but if we parsed some already, this is where we stopped */
    int    rsize;   /* total allocated size of rbuf */
    int    rbytes;  /* how much data, starting from rcur, do we have unparsed */

    char   *wbuf;
    char   *wcurr;
    int    wsize;
    int    wbytes;
    int    write_and_go; /* which state to go into after finishing current write */
    void   *write_and_free; /* free this memory after finishing writing */

    char   *ritem;  /* when we read in an item's value, it goes here */
    int    rlbytes;

    /* data for the nread state */

    /*
     * item is used to hold an item structure created after reading the command
     * line of set/add/replace commands, but before we finished reading the actual
     * data. The data is read into ITEM_data(item) to avoid extra copying.
     */

    void   *item;     /* for commands set/add/replace  */
    int    item_comm; /* which one is it: set/add/replace */

    /* data for the swallow state */
    int    sbytes;    /* how many bytes to swallow */

    /* data for the mwrite state */
    struct iovec *iov;
    int    iovsize;   /* number of elements allocated in iov[] */
    int    iovused;   /* number of elements used in iov[] */

    struct msghdr *msglist;
    int    msgsize;   /* number of elements allocated in msglist[] */
    int    msgused;   /* number of elements used in msglist[] */
    int    msgcurr;   /* element in msglist[] being transmitted now */
    int    msgbytes;  /* number of bytes in current msg */

    item   **ilist;   /* list of items to write out */
    int    isize;
    item   **icurr;
    int    ileft;

    /* data for UDP clients */
    bool   udp;       /* is this is a UDP "connection" */
    int    request_id; /* Incoming UDP request ID, if this is a UDP "connection" */
    struct sockaddr request_addr; /* Who sent the most recent request */
    socklen_t request_addr_size;
    unsigned char *hdrbuf; /* udp packet headers */
    int    hdrsize;   /* number of headers' worth of space is allocated */

    int    binary;    /* are we in binary mode */
    int    bucket;    /* bucket number for the next command, if running as
                         a managed instance. -1 (_not_ 0) means invalid. */
    int    gen;       /* generation requested for the bucket */
} conn;
</code></pre>

<h5>conn_new()</h5>

<p>ファイル記述子のイベント処理をlibeventに登録する。イベントハンドラはevent_handler()関数で、これはほとんどそのままdrive_machine()関数を呼び出すだけのもの。</p>

<h5>drive_machine()</h5>

<p>入力(または出力)が可能になるたびに呼ばれ、名前の通り、状態機械を駆動する。
状態によって分岐し、stopフラグがセットされるまでループする。このフラグはブロックせずにできる入出力を処理し終えたり、接続をクローズすることになった場合にセットされる。言い方を変えると何かしらやることがある間はセットされない。</p>

<p>状態の種類はmemcached.hで定義されている。</p>

<pre><code>enum conn_states {
    conn_listening,  /* the socket which listens for connections */
    conn_read,       /* reading in a command line */
    conn_write,      /* writing out a simple response */
    conn_nread,      /* reading in a fixed number of bytes */
    conn_swallow,    /* swallowing unnecessary bytes w/o storing */
    conn_closing,    /* closing this connection */
    conn_mwrite      /* writing out many items sequentially */
};
</code></pre>

<p>各状態で行われる入出力は大体どれも次のような形になっている。</p>

<ol>
<li>入力ならバッファの大きさや空きのチェック</li>
<li><code>res = read(...)</code></li>
<li><code>if (res &gt; 0)</code>: まだ入力が残っているかも知れないのでループの繰り返しへ</li>
<li><code>else if (res == 0)</code>: EOF。conn_closingへ状態を遷移</li>
<li><code>else</code>: errnoがEAGAINかEWOULDBLOCKならブロックせずに読めるデータがないのでstopフラグを立てる</li>
<li>libeventに登録している監視条件が状態に合致しているか確認し必要なら変更。状態遷移ではイベント監視条件の変更までは行わないため。</li>
</ol>

<p>主な状態を以下に挙げる。</p>

<h5>conn_listening</h5>

<p>新たにクライアントが接続してきたので<em>accept</em>して新しいファイル記述子のconnを作成。初期状態はconn_read。</p>

<h5>conn_read</h5>

<p>入力データを読み込んでバッファに追加。既に1行の読み込みが完了していれば(memcachedのプロトコルでは、最初の1行でコマンドを通知する。add等の場合はその後にオブジェクトのデータが続く)、コマンドを解析してそれぞれのコマンドの処理へ分岐する。</p>

<p>コマンドがadd等の場合、後続するデータを読み込むためにconn_nread状態に遷移する。</p>

<h5>conn_nread</h5>

<p>データ本体の前に通知されたバイト数まで入力を読み込む。所定バイト数の読み込みを完了すると、completion_nread()経由でstore_item()が呼ばれる。</p>

<p>コマンドの結果(<code>"STORED"</code>, <code>"NOT STORED"</code>, <code>"CLIENT_ERROR ..."</code>)をクライアントに返すため、状態はconn_writeに遷移する。</p>

<h5>conn_write, conn_mwrite</h5>

<p><code>sendmsg</code>を用いてデータを送出。全データの出力が完了すると状態はconn_readに戻る。</p>

<h4><span id="memcached_RubyMemCache">クライアントの例: Ruby-MemCache</span></h4>

<p>Rubyのmemcachedクライアントライブラリである<a href="http://www.deveiate.org/projects/RMemCache/">Ruby-MemCache</a>を覗いてみた。</p>

<p><a href="http://www.deveiate.org/code/Ruby-MemCache/">APIのドキュメント</a>を見ると主役はMemCacheクラスとServerクラスだけだ。</p>

<h5>MemCacheクラス</h5>

<p>c_thresholdやcompressionといったメンバがあり、キャッシュに載せるデータがある程度大きい場合は自動圧縮・伸長できるようになっている。</p>

<h5>サーバリストの設定方法 (<code>MemCache#servers=(servers)</code>)</h5>

<p>引数にサーバ(<code>"hostname:port"</code>か<code>"hostname:port:weight"</code>)のリストを一括して与えるようになっている。</p>

<h5>サーバの選択 (<code>MemCache#get_server(key)</code>)</h5>

<p>一見consistent hashingをしているのかと思ったが台数に依存する剰余をとっているので
そうではない。</p>

<p>サーバごとにメモリサイズに応じたウェイトをつけ、サーバが選択される可能性がウェイトに比例するようにしている。</p>

<p>ただしこのコードでは、あるサーバが死んでいるとその代替先はリスト中の次のサーバ一択となり負荷が集中してしまう上、ウェイトの値の大きさによっては代替先を選ぶコードが全く役に立たない。代替先の選択方法を変えるか、<code>@buckets</code>を生成後にシャッフルすることが必要だろう(シャッフルは全クライアントで同じ結果になるようにする)。</p>

<pre><code>def get_server( key )
    svr = nil

    @mutex.synchronize( Sync::SH ) {
        if @servers.length == 1
            self.debug_msg( "Only one server: using %p", @servers.first )
            svr = @servers.first
        else

            # If the key is an integer, it's assumed to be a precomputed hash
            # key so don't bother hashing it. Otherwise use the hashing function
            # to come up with a hash of the key to determine which server to
            # talk to
            hkey = nil
            if key.is_a?( Integer )
                hkey = key
            else
                hkey = @hashfunc.call( key )
            end

            # Set up buckets if they haven't been already
            unless @buckets
                @mutex.synchronize( Sync::EX ) {
                    # Check again after switching to an exclusive lock
                    unless @buckets
                        @buckets = []
                        @servers.each do |svr|
                            self.debug_msg( "Adding %d buckets for %p", svr.weight, svr )
                            svr.weight.times { @buckets.push(svr) }
                        end
                    end
                }
            end

            # Fetch a server for the given key, retrying if that server is
            # offline
            20.times do |tries|
                svr = @buckets[ (hkey + tries) % @buckets.nitems ]
                break if svr.alive?
                self.debug_msg( "Skipping dead server %p", svr )
                svr = nil
            end
        end
    }

    raise MemCacheError, "No servers available" if 
        svr.nil? || !svr.alive?

    return svr
end
</code></pre>

<h5>サーバの生死判定 (<code>Server#alive?</code>)</h5>

<pre><code>def alive?
    return !self.socket.nil?
end
</code></pre>

<p>クライアントは全てのキャッシュサーバと接続を張りっぱなしにしておく。</p>

<h5><code>Server#socket</code></h5>

<p>サーバとの接続を返す。まだ接続していなかった場合は接続を張る(応答がなさそうならタイムアウト)。サーバの死亡が検出されると一定時間はいちいち再接続を試みないようにしている。</p>

<pre><code>def socket

    # Connect if not already connected
    unless @sock || (!@sock.nil? &amp;&amp; @sock.closed?)

        # If the host was dead, don't retry for a while
        if @retry
            return nil if @retry &gt; Time::now
        end

        # Attempt to connect, 
        begin
            @sock = timeout( @connect_timeout ) {
                TCPSocket::new( @host, @port )
            }
            @status = "connected"
        rescue SystemCallError, IOError, TimeoutError =&gt; err
            # $deferr.puts "Error while connecting to %s:%d: %s" %
            #  [ @host, @port, err.message ]
            self.mark_dead( err.message )
        end
    end

    return @sock
end
</code></pre>

<h4><span id="memcached_libketama">libketama</span></h4>

<p><a href="http://www.last.fm/user/RJ/journal/2007/04/10/392555/">libketama</a>はconsistent hashingを利用したライブラリ。他のmemcachedクライアントライブラリを完全に置き換えるものではなく、機能はキャッシュサーバ選択に限定されている。</p>

<p>このページにはSubversionレポジトリのURLも書かれてはいるが接続できなかったので、ketama-0.1.1.tar.bz2をダウンロードした。</p>

<p>ketamaではハッシュの値域をcontinuumと呼び、0から2<sup>32</sup>までとしている。
この空間に、サーバごとに100～200個の点をばらまく。
キャッシュサーバの選択はキーのハッシュ値以上で一番近い点のものを選ぶ。</p>

<p>サーバのリストは次のようなファイルで与える。サーバごとに、IPアドレス・ポート番号・メモリサイズの組で指定する。メモリサイズはcontinuumにばらまく点の数を増減するウェイトになる。</p>

<p>重要な構造体はmcsとcontinuumの2つ。</p>

<h5>mcs</h5>

<p>continuum上のサーバ点を表す構造体。点がとる値とサーバのアドレス(ポート番号も含む、以下同様)からなる。</p>

<pre><code>typedef struct
{
        unsigned int point;  // point on circle
        char ip[22];
} mcs;
</code></pre>

<h5>continuum</h5>

<p>continuumを表す。ばらまいた点の数、サーバリストファイルの更新時刻、点の配列からなる。</p>

<pre><code>typedef struct
{
        int numpoints;
        void* modtime;
        void* array; //array of mcs structs
} continuum;

typedef continuum* ketama_continuum;
</code></pre>

<h5>ketama_get_server(char* key, ketama_continuum cont)</h5>

<p>キャッシュサーバを選択する。</p>

<p>まず、キーからハッシュを計算する。
次に、<code>cont-&gt;array</code> (mcsのソート済配列)を二分探索してサーバを探す。</p>

<h5>ketama_create_continuum(key_t key, char* filename)</h5>

<p>continuumを作成する。</p>

<p>サーバリストを読み込み、メモリを確保するとサーバに対応する点をcontinuum上に配置する。</p>

<p>点の総数はサーバの数×160で、サーバのメモリサイズに応じて割り振られる。
サーバのアドレスに"-1"等の番号をくっつけたもののMD5をとって乱数とし、一つのMD5を元に4つの点を決める(128ビットのMD5を32ビット×4に分割)。
点を生成し終えると二分探索に備えてソートしておく。</p>

<p>生成したcontinuumは共有メモリに置かれる。引数keyはそのためのキー。</p>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/71425025.html">
<link>http://smpl.seesaa.net/article/71425025.html</link>
<title>Consistent Hashing</title>
<description>Tom White's Blog: Consistent Hashing(翻訳: ConsistentHashing - コンシステント・ハッシュ法)でconsistent hashingというアルゴリズムを知った。大略こういうことらしい。問題複数台のキャッシュサーバがあり、オブジェクトのキャッシュをどのサーバに配置するかを決めたい。普通のハッシュ利用法だとどうなるかオブジェクトのハッシュ値 mod N (Nはキャッシュサーバの台数)で決める。しかし、これではキャッシュサーバ...</description>
<dc:subject>雑記</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2007-12-07T13:00:54+09:00</dc:date>
<content:encoded><![CDATA[
<!-- *sblog-encoded markdown/euc-jp+gzip+b64*
H4sIAHPFWEcAA4VTTW8SURTdz6+YnXTRmdaNCcGa6MZVVyYujDGIhBlbGFNGiD9n4D5MZEyJRadA
26nMhI8ZGNpNXfgRG2uFoaYJ1sREF943QwELjW/Bey+cd+4599wJRUQ5unTvjhRn7wp4vJJkb65K
sSB7S0okxaQcTcjs7XBSEBOx+wFBlp8EeT4dfYiQJPc4nApziajM0ysvS/E0ZeDDaxFBTEX5qwsL
1/jFRT4yYnogIBMnyPHVuRDvFQ7YH1s/g2zIVzGuOSzJzrNkg/wgGimRGu4NJUc6xMD7V1sZ60mn
OeHZ0xWRi0hxnh7S4orI0x8uEhNvTNGeV5+D+lgcK/h/QgMAciRD+qRIXFImn2FgOGCArmSr3xwV
NiBHEbg3oAcagJJlmNZR1WWu42KsrH5WPQaHvCRffKnktbJF9kCFDLhKhrwgB2STPCfrpDGJggE0
8e5j7fp74wBK0Ic8DLQuHILu12kemi1EDZvQ/tXetxXYRSVNVGRRPDP/38Vc0DBkM9psXHrEhlLh
taXlEE83NjB5g86UKad6rJ9hHz2NfaoQO5IHTclgf06gPuPJwGy+MhClg1s+3f7jnzyvjbdFqKGe
SzpEe7LbBdXah1PMo4YvP0GO1rR38IRJgAMd5l+ns6uXUNte8Xd1c9TxPPR21pGjDKalGkVQdY0y
nudR+FArUFfTqW51wUUXeWREzoqJXkYpNnuUyVIrb7Af23BSsDYKl/tDbx3PWw+TBHzhTSJ6UqHr
pz/9USKm483d9Jrh3U8JGW0lQ6NexqHTvGJZMLGF7mSqaLqNDcJ4tHeVygTSC5jawXjp2B3RMGYV
M/1YHRzQ74jLK9m/u51nYm8EAAA=
-->
<p><cite><a href="http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html">Tom White's Blog: Consistent Hashing</a></cite>(翻訳: <cite><a href="http://www.hyuki.com/yukiwiki/wiki.cgi?ConsistentHashing">ConsistentHashing - コンシステント・ハッシュ法</a></cite>)でconsistent hashingというアルゴリズムを知った。大略こういうことらしい。</p>

<h4>問題</h4>

<p>複数台のキャッシュサーバがあり、オブジェクトのキャッシュをどのサーバに配置するかを決めたい。</p>

<h5>普通のハッシュ利用法だとどうなるか</h5>

<p>オブジェクトのハッシュ値 mod <var>N</var> (<var>N</var>はキャッシュサーバの台数)で決める。</p>

<p>しかし、これではキャッシュサーバを追加したり削除したりすると全てのオブジェクトのキャッシュ配置先が変わってしまう。</p>

<h5>望ましいのは</h5>

<p>キャッシュサーバを追加すれば既存のサーバから少しずつ分担が新しいサーバに移動し、キャッシュサーバが取り除かれれば残りのサーバに負担が分散、それ以外のオブジェクトのキャッシュ先は変わらない、というのがよい。</p>

<h4>Consistent Hashingとは</h4>

<p>キャッシュサーバを決めるのに、mod Nをしない。つまりサーバの台数に依存した計算をしない。しかしそれでどうやってキャッシュサーバを決定するのだろうか。</p>
<a name="more"></a><!-- *sblog-encoded markdown/euc-jp+gzip+b64*
H4sIAHPFWEcAA71W227bRhB951fwMRFkQeljUPQP8hGW44e+9Lm/Q2WWCWw6UqK7LFniRVdaSwUo
FAgRgtpJYesCuFVbIwWSomeXZCibcouiRQjosrfZmXPOzLD4szvrHNCQZRmxHssqO7cehXnMZhX2
kz0ibo/yl+SyI/bOn2MlrcHGtB40qdZp07r3xp5QjZbaE/6OntG4oRFvvSVOl6TTsPxL7YKIXO2J
oqh4Hn37nfrvn0e738vTX/v+pXZ2Epv+pqKfxI3phPz+Ro3iKf9enCgblndVVTc3xhn/Zw8f3Yqm
H6u6rar7fgy7yUxyL5lKpR6q7IBNWJU9ZTnWl2u6mdStpG4Hy7dQU5TNA+RRk/gL3dLJpSwt6z06
L58RxbB2JcYlWtGSCtTHbom3Qg69pQXA7pEnYJeQuzSUi0oCjqr7avYATmGQSSIqMbAweCz/2Ypy
b196scLRhe9J7HYDBgnGe+3LrkHu6RnZ1NVNWpsHNLmvKINmtUnr4SuQv6S+c2Lmid82AqEc0lz4
uGVF+G8IAGJXc7vYuS5fFifUr7aFfRFX3EFn8NKW8LRormUFqLCKgKQnm4AbrQpVAHAAa34mLQaK
BYi3TetOZFzLbprKkIHF0MI/SFt3/ou0E19Q2pmHqh9WN78N6PpV82MEdDSKKxZ02pi/ofb8ihyp
sZgIQiD/hgkruv0mE3tgwv6yTOz8f0zc0637W5nYAxN2yMSLbtkkPjSOy5o+mL+cEi+/6vwRq9tB
9QY7FaRbPJO5+alal6mGDKbzeu/kedUSe6OdYAk3Yk7kvIFkKoDfLXxspCa3LugIvLj2B5FcwzEt
yGhXqYpygJPS0iGqFpFeap2+J6NzbRaaY3iQ8ylDwhawc4DihboW7qU+eL771rXVav1G/OQ5biY5
49anOJsbGoX3VJFlYEVzsUOuGjTGSHzc7p+0qK6lf7Jgsjn7lbXZj0GdlbX17t3wzoP1reUqvGs0
0mror3etu3zanslYQ4siXlR23CvwNvDN0Z2XrMgWrA7PeL0HlPnodactEcO6sCRj9SLhiU4dxrCl
0K4DP4qih4hsDPjJiZNm0eLkdjyhDYFtHBXZe3oNC+Ve9qLAE/3zmot4cDrOm9Qdlyr2TmfNcWdK
uTswcoXKQ/v+G0W8DjVQ3FELDmVH7EN9nuAjRD9WO5BHpTeNT37t2NyJLhatOJoDW0HzRFNqRQhs
vucgu84QqVDXCT4hp2EWeFI1DrR9jp7JP+twjfegKzqmVixveO2i8po860Lo2dWspxLHhfDT+dCZ
ikz3m7rE2Ch+tH4Ab0eBnwNYz8E+CeSgBuEhh4IoyjqRSZUr4qWj42vB+IP0lgrMT1bwsPcgndbM
r9JpcbN/+nSWt2jdeIbc7mKMW/8CdnVTk0AKAAA=
-->
<h5>基本的なアイデア</h5>

<p>ハッシュ値の値域にキャッシュサーバを表す点を配置する。例えば次の図のような具合いに。</p>

<pre><code>Min                                                   Max
&lt;------.--*------------.------.---*-----------.-*---.-&gt; ハッシュ値空間
       a  α           b      c   β          d γ  e

a,b,c,...: オブジェクト
α,β,γ,...: キャッシュサーバ
</code></pre>

<p>オブジェクトはその右側にある最も近いキャッシュサーバに配置されることにする。
つまり図では次のようになる。</p>

<ul>
<li>a, e → α</li>
<li>b, c → β</li>
<li>d → γ</li>
</ul>

<p>(eはそれより右側にキャッシュサーバがないので先頭に戻ってαを選ぶ)</p>

<p>表現を変えると直前のキャッシュサーバ点から次のキャッシュサーバ点までがあるキャッシュサーバの担当区間と言える。</p>

<p>キャッシュサーバが追加されたら、その点より前のオブジェクトが新しいサーバに移る。
次の図ではキャッシュサーバδが追加され、オブジェクトbがδに移る。</p>

<pre><code>Min                       δ                          Max
&lt;------.--*------------.--*---.---*-----------.-*---.-&gt; ハッシュ値空間
       a  α           b      c   β          d γ  e

b: δに移動
</code></pre>

<p>キャッシュサーバが削除されたら、削除されたキャッシュサーバにあったオブジェクトは一つ右側のキャッシュサーバに移る。次の図ではキャッシュサーバβが削除され、オブジェクトcがγに移る。</p>

<pre><code>Min                       δ                          Max
&lt;------.--*------------.--*---.---------------.-*---.-&gt; ハッシュ値空間
       a  α           b      c   (β)        d γ  e

c: γに移動
</code></pre>

<h5>影響の分散・負荷の均等</h5>

<p>たしかにキャッシュサーバの増減があっても最小限しかキャッシュは移動しないが、これではキャッシュサーバの担当区間の大きさに著しい偏りが生じるではないかという疑問が当然出てくる。</p>

<p>そこでどうするかというと、キャッシュサーバの担当区間を多数の小さい区間に細かく分割し、それらの小区間がばらばらに入り交じるようにランダムに配置する。ばらばらに入り交じるようにとは、あるキャッシュサーバの小区間が様々なキャッシュサーバの小区間に隣接するようにということ。これがこのアルゴリズムの最大の要点。</p>

<p>この区間分割はハッシュ値域に配置するキャッシュサーバ点をサーバごとに一つではなく値域全体に渡って多数ランダムに配置することで実現される。</p>

<p>こうすることによって、キャッシュサーバ増減の影響は目出度く様々なキャッシュサーバに分散される。例えばキャッシュサーバが取り除かれるときはある小区間がキャッシュサーバγに吸収され、ある小区間がαに吸収され……となる。</p>

<p>またランダムに点を配置するためにそれぞれの小区間の大きさはばらつくものの、それらを合わせた担当区間の大きさの合計は大数の法則によりある程度均等になることが期待できる。</p>

<p>どれくらいに分割するのがいいかというと、件の記事では10のキャッシュサーバの場合で100～200程度という目安を示している。</p>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/46185956.html">
<link>http://smpl.seesaa.net/article/46185956.html</link>
<title>Designing BSD Rootkits</title>
<description>表題の書籍を読んだ。An Introduction to Kernel Hacking との副題の通り、悪いことをするための本というよりはそれをネタにしたFreeBSDカーネルへのいざないという感じの本。以下に紹介するようにカーネルコードに密着した話題だけが扱われている。カーネルのソースコードに手を出してみたい気持ちはあるものの、難しそうだとか先に知らないといけない事が多そうで敷居が高そうだと感じて先へ進めない読者(筆者のことです)のハードルを下げるには丁度具合のいい本だと思...</description>
<dc:subject>読書メモ</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2007-06-29T06:43:32+09:00</dc:date>
<content:encoded><![CDATA[
<!-- *sblog-encoded markdown/euc-jp+gzip+b64*
H4sIAMoqhEYAA01SXU/bVhi+51dYuYIL4oQOTaOA1HWaVq2aKrGr3SAT3OAtjpljRrd/4/Cciilh
hPBVNw7J4tSOY3CqCvVrnWiDKmjSlhGkatrUaa9Nw2bJ9vl43+frnPFZ6Qcuo/2YEiciN1OKoI2p
UnJOu8zNKOqsqI7FoqOifDkyOS5wc6p4cyIyp2nzYzy/uLgYFWThJyUdTSjRb+d58ZaY4JUZaVbJ
8Femrn3Fx0c/uTTycfyjkVF+RsosSMPx4ZERApLkZJ+wzzF/K8Jl1MQFuJqQhyVZSIqZ/zhk/nyF
v/E/5GgsHp2euh6Pxaanrn79zfkzTXKSEU5IaRORG6qQ0KSEkOKuKrKspLnrUmY+wk+O8wK95H1y
YMAt1zrwy6fVB+jZLs5Q1ZcGBq6kuWtpTVVmFwiAGjWF+1JU02KK+0JIfCelkxwa8N2/g976Ljp6
trACYJ1WezDQRQUv4HvPaA7cxjE6aKGM1+ixPVaBh21UPldF8dOpz1hON2mxi8fwqXgLTfqGbRtb
uBOA6EuFZtGEt/Og+EcIfkyQXr+PrdPfhbf7s/UygG1ZJKqKNeQL2zghSpuwuoGpCyaflWlk9DvN
Dnrl+9Rr44CEYxOl9/iVBGeJq02qfD1rn9J+mXirpC1XPYZn+Xh1oXUtGJXeIV+rhFWOe7B5grzR
7veEXmzqe1xpUjRUbbvm/qD7i7lPBEFuDowhUtYKNJHGHjkuEr+HlvXXvSdbp8ZRmA8oVMIrPcJt
fSl+KcaeUsOd+p/3nqDhvMOqnjX2sGz8g+f4DW4oO5C4jjdkpYnVwdGdFlr+4XphaBPbT+HRob9A
JcjHbu/ZaJk68RwH6KzB9pnFVj1yG+55teUgYasEsEfsDSuwV+w58vUzc5lCO0/a1rNmrpmHY9bu
/g6v9PZDrt3QJK1jhcY+Wo2znRNCPKI59CXKvUqVLlXlNh4WDrFMVY3aUc1nHfaQLDaCk3e83WeU
SDNEcPTs96K8sPMS3kZ9awW9u29hwSZGo/memOgZtC14ZLjQNA6GyGLRrGwEIZCStp5l3RA3T+f8
GveJ+Yz2ApXtYt0p09hH7sMFDu6Tg7a1Vj6km7br9ALN/wIiBdZHPgQAAA==
-->
<div style="float:right; border:0.5em;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/1593271425/bisui-1-22"><img style="border:0px" src="http://rcm-images.amazon.com/images/P/1593271425.01._SL100_SCTZZZZZZZ_.jpg" alt="Practical Common Lisp"/></a></div>

<p>表題の書籍を読んだ。</p>

<p>An Introduction to Kernel Hacking との副題の通り、悪いことをするための本というよりはそれをネタにしたFreeBSDカーネルへのいざないという感じの本。以下に紹介するようにカーネルコードに密着した話題だけが扱われている。</p>

<p>カーネルのソースコードに手を出してみたい気持ちはあるものの、難しそうだとか先に知らないといけない事が多そうで敷居が高そうだと感じて先へ進めない読者(筆者のことです)のハードルを下げるには丁度具合のいい本だと思う。130ページ程度と薄く、考え込むほど難しいところもなく(5章は例外)気軽に読めた。</p>

<p>内容は次のよう。トピック毎に内容に即した短いプログラムが提示されていて、自分で実際に試してみることができるのは非常に大きい。ただしどこか間違えると大体リブートする破目になるので、qemu上に環境を作って試す方がいい(特に5章以降)。
下心なくとも、ルートがあればこんなことも可能なのかというだけでも単純に面白い。</p>
<a name="more"></a><!-- *sblog-encoded markdown/euc-jp+gzip+b64*
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
-->
<ul>
<li>1章 <br/>
カーネルローダブルモジュール(KLD)の作り方。
システムコールやキャラクタデバイスを登録するモジュールも。</li>
<li>2章 <br/>
システムコールのフック。システムコールのテーブル(<code>sysent[]</code>)で関数ポインタを差し替える。カーネルには他にも関数ポインタが登録されたテーブルがあり応用可能。</li>
<li>3章 <br/>
カーネル内のデータを直接書き換え、実行中のプロセスを隠蔽。<code>proc</code>構造体とそのリスト、またカーネル内のデータを操作する際に必要なロックについて説明してくれる。同様にしてオープンしているTCPポートの隠蔽も。</li>
<li>4章 <br/>
デバイスのエントリポイントをフック。2章、3章の応用で、新しいことは何もない。たったの4ページ。</li>
<li>5章 <br/>
実行中のカーネルのコードを書き換え、システムコールを乗っ取る。直接上書きする他、カーネル空間で確保したメモリに置いたコードへジャンプさせる手も。libkvmによるカーネル空間へのアクセス方法について説明してくれる。
この章では、公開されているソースコードをコンパイルして実行するだけなら簡単だが、説明された手順を自分で追体験すると多少厄介。</li>
<li>6章 <br/>
tripwireのようなホストベースIDSに検出されないことを目指して5章までの手法を適用。<code>execve</code>システムコールをフックして特定のプログラムの実行を別の不正なプログラムにすり替え。そのプログラムの発見を防ぐために<code>getdirentries</code>システムコールをフック。またタイムスタンプからファイルのインストールが露見するのを防ぐため、タイムスタンプを変更するコードを一時的に無効化。</li>
<li>7章 <br/>
6章までで扱ったようなルートキットをどう検出するか。検出する側も同じ技術で立ち向かう。著者の結論としては、検出は必要であるが容易ではなく、そもそも侵入されるなということらしい。この章だけは主に説明のみ。</li>
</ul>

<p>扱われているOSのバージョンはFreeBSD 6.2 Release。</p>

<p>この手の本は対象とされるバージョンと現行のソースコードが乖離してきたり、そうでなくともバージョン番号が離れてくるとなんとなく読む気が失せてくるので、興味があるなら旬を逃す前に手を伸ばすのがいいと思う(6.xの間は大丈夫だろうが)。</p>

<p>C言語の知識と、基本的な命令が読める程度の初歩的なx86アセンブリの知識が必要。</p>

<p>あくまでいざないなのでカーネル内のデータなどについては題材の即した部分しか扱われず、物足りなさを感じるかも知れない。尤もそれならしめたもので次は悪魔本なりソースコード自体なりへGo!ということだろう。</p>

<h4>関連リンク</h4>

<ul>
<li><a href="http://nostarch.com/frameset.php?startat=rootkits">書籍の公式ページ</a> (出版社のサイト): 掲載ソースコードのダウンロード、サンプルとして2章のPDFがある。</li>
<li><a href="http://www.onlamp.com/pub/a/bsd/2007/05/31/defending-against-rootkits-under-bsd.html">著者のインタビュー</a> (onlamp.com)</li>
<li><a href="http://bsdtalk.blogspot.com/2007/05/bsdtalk113-designing-bsd-rootkits.html">著者のインタビュー</a> (bsdtalk)</li>
<li><a href="http://www.freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/">FreeBSD Developers' Handbook</a></li>
</ul>

<div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/1593271425/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://rcm-images.amazon.com/images/P/1593271425.01._SL100_SCTZZZZZZZ_.jpg" alt="Designing BSD Rootkits: An Introduction to Kernel Hacking" style="border: none;" alt="no image" /></a></div><div class="amazlet-info" style="float:left;margin-left:15px;line-height:120%"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/1593271425/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank">Designing BSD Rootkits: An Introduction to Kernel Hacking</a><div class="amazlet-powered-date" style="font-size:7pt;margin-top:5px;font-family:verdana;line-height:120%">posted with <a href="http://www.amazlet.com/browse/ASIN/1593271425/" title="Designing BSD Rootkits: An Introduction to Kernel Hacking" target="_blank">amazlet</a> on 07.06.29</div></div><div class="amazlet-detail">Joseph Kong <br />No Starch Pr (2007/04)<br /></div><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/1593271425/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jp で詳細を見る</a></div></div><div class="amazlet-footer" style="clear: left"></div></div>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/37757606.html">
<link>http://smpl.seesaa.net/article/37757606.html</link>
<title>Garbage Collectionの教科書</title>
<description>この本はほぼ唯一といっていいgarbage collectionの教科書。初歩的なアルゴリズムから並列GCまで幅広く、複数のアルゴリズムがあるものはそれぞれ解説して利点欠点も比較している。Garbage Collection: Algorithms for Automatic Dynamic Memory Managementposted with amazlet on 07.04.04Richard Jones Rafael Lins John Wiley &amp;amp; So...</description>
<dc:subject>GC</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2007-04-04T19:14:16+09:00</dc:date>
<content:encoded><![CDATA[
<p>この本はほぼ唯一といっていいgarbage collectionの教科書。初歩的なアルゴリズムから並列GCまで幅広く、複数のアルゴリズムがあるものはそれぞれ解説して利点欠点も比較している。</p>

<div class="amazlet-box" style="margin-bottom:0px; font-size: 9pt;"><div class="amazlet-image" style="float:left;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://images-jp.amazon.com/images/P/0471941484.09._PIsitb-sm-arrow,TopLeft,25, -18_OU09_SCMZZZZZZZ_.jpg" alt="Garbage Collection: Algorithms for Automatic Dynamic Memory Management" style="border: none;" /></a></div><div class="amazlet-info" style="float:left;margin-left:15px;line-height:120%"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank">Garbage Collection: Algorithms for Automatic Dynamic Memory Management</a><div class="amazlet-powered-date" style="font-size:7pt;margin-top:5px;font-family:verdana;line-height:120%">posted with <a href="http://www.amazlet.com/browse/ASIN/0471941484/bisui-1-22" title="Garbage Collection: Algorithms for Automatic Dynamic Memory Management" target="_blank">amazlet</a> on 07.04.04</div></div><div class="amazlet-detail">Richard Jones Rafael Lins <br />John Wiley &amp; Sons Ltd (Import) (1996/07/12)<br />売り上げランキング: 3600<br /></div><div class="amazlet-review" style="margin-top:10px; margin-bottom:10px"><div class="amazlet-review-average" style="margin-bottom:5px">おすすめ度の平均: <img src="http://images-jp.amazon.com/images/G/09/x-locale/common/customer-reviews/stars-5-0.gif" alt="5.0" /></div><img src="http://images-jp.amazon.com/images/G/09/x-locale/common/customer-reviews/stars-5-0.gif" alt="5" /> よくまとまっていて分かりやすい<br /></div><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jp で詳細を見る</a></div></div><div class="amazlet-footer" style="clear: left"></div></div>

<p>米Amazon.comのページを見たい方は<a href="http://www.amazon.com/Garbage-Collection-Algorithms-Automatic-Management/dp/0471941484/">こちら</a>。当然ながらカスタマーレビューが日本のアマゾンより多い。</p>

<p>なまじ良く書かれているだけに、Amazonの太っ腹機能「なか見!検索」(検索した単語のある周辺のページを立ち読みさせてくれる)がかなり便利に使える。アルゴリズム名などで検索するのがお勧め。検索して出てきたところから2～3ぺージも読めば用が足りることも多い。</p>
<p>例えば、マルチスレッド・プログラム用のGC (concurrent GC)としてTreadmillという方式がある。
これで検索すると p.188, p.218, p.219, p.220, p.221, p.225 が出てくる。これはもう明らかに、p.218～221辺りを読めばいいわけだ。案の定、p.218をクリックすると、そこから「8.8 Baker's Treadmill collector」という節が始まっている。</p>
<p>目次も詳しい。そのおかげで、検索のアタリをつけるのも簡単。いいんだか悪いんだか。</p>

<p>※目次などを除いて、「なか見!検索」で出てきた本文中のページを立ち読みするにはアマゾンのアカウントでログインしていることが必要。ページをクリックして「この機能は利用できません」のように表示されたら、おそらくアカウントでログインしていないことが原因です。</p><a name="more"></a>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/37446952.html">
<link>http://smpl.seesaa.net/article/37446952.html</link>
<title>copying GCに対する改良</title>
<description>前回の記事ではcopying GCを取り上げた。今回はその続きとしてcopying GCに対する改良を2つ取り上げる。2つはそれぞれcopying GCの短所、つまり「毎回全てのオブジェクトがコピーされること」「確保したメモリの半分しかプログラムで利用できないこと」に対応している。 大きい、または長命なオブジェクトの別扱いcopying GCは生き残るオブジェクトを毎回全てコピーするので、オブジェクトが短命でかつ小さい程都合がよい。裏を返すとオブジェクトが大きかったり長命だと...</description>
<dc:subject>GC</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2007-04-01T11:42:42+09:00</dc:date>
<content:encoded><![CDATA[
<!-- *sblog-encoded hatena/euc-jp+gzip+b64*
H4sIALMoD0YAA4VUS08aURTe8ytm1YXGTbvrtov+DmOxMY1C0MT058zwXdvoUHkGh9co4IAzckcT
g91gkKQRGRqTIWlMGk3PXGBkiNYdc+8553uc73KUTI/Ac4niHzRgr0SiX9c2PksfP8At9TGo/EIa
uhwvxKnKRhm8mkECTeRhzNRa1TNocNKP/ATuW9T9VkeOh0J0IHqHqGA408VrxfJAVuj2CgO5bfXT
o6MsDHC2y9rsgH1j+6wJlWXYhVxCjvodZNCUL+V2hpsd4qCza9ZjA/BmxVTpe49dst8syW5ZF43W
feucNCVgAuM+j2fq0eNOJ4LaglS9oQoQiyvosOvaqQ4zgM/N02QeCIVmmNuHB0gU63ACle5UwYQx
OQKOhqwE9dSKhNHAHuqVH6QK9fvjC+0GKvrEI84VuGabepvBLsFzDzViORAsD6kiwzTvTh+h66kh
jzMenrcnmrYzp8Q2TzlP9mHR/gbry7Ev0htpczscjqKRPWk9CL7kE92TQ7Iyu6nqWaVD6LewM0nh
X5782hEOLkhMYQ7LsgErsG6IOI3YOeHl2Q2jZBU6sMiBDgOd61CfwRWbDTpZ/AmtMiRXbHYll9g+
ekaq+AhLuxij+vO4pwXf4dRasEsy+LvSHe/CojikiO5wvOZFaTUWWV/ajC6vhEmeQnBiRUfJubD5
wMEWcUxJDLiZ/QtrJptepvRJ6/PmvoAUmj4NWBTOAxjlPtULMWKJFgVYqFh6YmT0iPgrEt6HFhel
rchU9bhcVlZj4bBvn6ubuIbjVa5GYtvLsU/exqORtY2tcAxuLmHr3t1Tvabp+Tkiz5mjPplTOAf3
6cwPmjVKZOx/WrxYB68n4cgHn5gwxwcqGDnleBeW52owOiIpPREYGuDQqOb0UlbmjRM71l+CkRVP
yWs1lFWKHlT6dTI2Bb3xso0ULd5f9ESWmx+hRfOCL0ot7+JO/A/cjlH9oHHxunl6VHoQU93Ja/kH
ThnC1OMFAAA=
-->
<p>前回の記事ではcopying GCを取り上げた。今回はその続きとしてcopying GCに対する改良を2つ取り上げる。</p>
<p>2つはそれぞれcopying GCの短所、つまり「毎回全てのオブジェクトがコピーされること」「確保したメモリの半分しかプログラムで利用できないこと」に対応している。</p>
<h4> 大きい、または長命なオブジェクトの別扱い</h4>
<p>copying GCは生き残るオブジェクトを毎回全てコピーするので、オブジェクトが短命でかつ小さい程都合がよい。裏を返すとオブジェクトが大きかったり長命だとコストが嵩む。</p>
<p>そこで、そのようなオブジェクトは別領域にとりmark &amp; sweepで管理することにして、copying GCの対象からは外してしまう。</p>
<h5> アルゴリズム</h5>
<p>コンパクションの際に、ポインタがmark &amp; sweepで管理されるオブジェクトを指す場合はマークも同時に行う。</p>
<p>ポインタのとりえる値は次の3種類に分けられる。</p>
<ol>
<li>from-spaceにあるコピー前のオブジェクトを指す場合</li>
<li>from-spaceを指すがオブジェクトは既にコピーされていた場合</li>
<li>mark &amp; sweepで管理するオブジェクトを指す場合</li>
</ol>
<p>それぞれに応じて処理は次のようになる。</p>
<ul>
<li>from-space内のコピー前のオブジェクトを指す場合:
<ol>
<li>to-spaceにコピー、freeポインタを進める</li>
<li>forwarding pointerを記録</li>
<li>ポインタを更新</li>
</ol></li>
<li>from-space内を指すがオブジェクトが既にコピー済の場合:
<ol>
<li>ポインタを更新</li>
</ol></li>
<li>mark &amp; sweep対象のオブジェクトを指す場合:
<ol>
<li>そのオブジェクトをマークし、オブジェクト内のポインタを再帰的に処理。</li>
</ol></li>
</ul>
<p>ポインタの種類も3種類、ルートポインタ、to-spaceにコピーされたオブジェクト内のポインタ、markされたオブジェクト内のポインタ、とあるが、どの場合も処理は同じになる。</p>
<p>マークを兼ねたコンパクションが終わったら、mark &amp; sweepの領域の回収処理を行う。</p>
<a name="more"></a><!-- *sblog-encoded hatena/euc-jp+gzip+b64*
H4sIALMoD0YAA62SX2/SYBTG7/kUTS+MfwINuzSDm13sczS1w0ZaGtpp/Dgdzzt1sgxlWyFMYLMw
LFDEGIgXM2okOMCYgCbGxMRTuk24NPGqb97nnOf5ndP3JsfAZmyPjdhH+lbYGC7L06lLdzYbslko
RMonNoJXfdKawSvYsIF953AXp+hYW3nPPaPTLsaXdY2ym6Wap7lJ7R2apUa7O9ddQErpDxUtwa2v
oVPL0r2NGs4BK4MXyOPU2lrOprRf3mNMD8pVG02qrl9mtH+2u4FWHuA5xu7vpoWsM8vVcYAJ6gDG
ViYUIlfvaETanJW9sY5YH9MVOP4FIe1pt6LwXn13s4c/9ntoutn8ZxT9XiLysI1j1mdfWY6dsw/+
LPiGEhFP/GlI70TJqYpnPnrJAYFpBWfBr7PQnfWZg035ZLSDORMaeEm8O+RX8TOveqdmKmzooiRT
hU3sC06loIc6rqrnrLnJBc50I51S/3ajvrD5afEttq1MgHtdC0dvLCNbvZMhQYLQ3hNSxym2KvQo
dliPFdgj+jUNeG4rR4/A6sMrT4pDCv9C8U1VTN/jrnHGA1nW4Z28Lp9ReDEYN74aWr2j3OekpGgY
MX5DSWymZT6+dCmJuqmkND6+vnacWxVIIV1RE5yRlmL8XdPUbwuCoerJyKYeMWTZEMWIJpvCxXDh
hCQompSWVVkzxWRYSqm0AN8xGtG1BM+JSTPG+968QMaB/z9RFQb/k2pliaowWKCibf0BDAixDpoD
AAA=
-->
<h4> インクリメンタルなコンパクション</h4>
<p>メモリの制約の厳しい環境では、確保できるメモリの半分しか一度に使用できないcopying GCは適用しづらい。そこで、コンパクションの効率を犠牲にしてメモリの利用効率を上げる方法が提案されている。</p>
<p>この手法では、ヒープを2つではなくn+1の部分空間に分割する。そのうちプログラムに使わせられないのは1つだけで、残りのn個の部分空間はプログラムが利用できる。</p>
<p>GCでは、とっておいたその部分空間をto-spaceとし、プログラムが使っていた部分空間のうち一つだけをfrom-spaceとしてcopying GCを行う。残りの(n-1)個の部分空間は「大きい、または長命なオブジェクトの別扱い」の場合のようにmark &amp; sweepの対象とする。</p>
<div class="figure">
<div class="caption">GC前</div>
<img src="http://smpl.up.seesaa.net/copying-gc/incremental-compaction1.png" alt="GC前"/>
</div>
<div class="figure">
<div class="caption">GC後</div>
<img src="http://smpl.up.seesaa.net/copying-gc/incremental-compaction2.png" alt="GC後"/>
</div>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/36160135.html">
<link>http://smpl.seesaa.net/article/36160135.html</link>
<title>Copying Garbage Collector</title>
<description>copying GCでウェブを検索すると真っ先に東大のコンパイラ演習の資料*が出てくるが、恥ずかしながらこれを読んでもいまひとつはっきりしたイメージが湧かず、うまくいくという確信がなかった。最近ようやく腑に落ちたのでまとめてみようと思う。    copying GCではヒープを2つの部分空間に分け、プログラムはその片方だけを利用して動作する。そこに空きがなくなるとGCが起動する。GCは使われている部分空間(from-space)の中の到達可能なオブジェクトを全てもう一方の部分...</description>
<dc:subject>GC</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2007-03-17T10:48:33+09:00</dc:date>
<content:encoded><![CDATA[
<p>copying GCでウェブを検索すると真っ先に東大のコンパイラ演習の資料<sup><a href="#copyinggc-ref">*</a></sup>が出てくるが、恥ずかしながらこれを読んでもいまひとつはっきりしたイメージが湧かず、うまくいくという確信がなかった。
最近ようやく腑に落ちたのでまとめてみようと思う。</p>

    <p>copying GCではヒープを2つの部分空間に分け、
プログラムはその片方だけを利用して動作する。そこに空きがなくなるとGCが起動する。
GCは使われている部分空間(from-space)の中の到達可能なオブジェクトを全てもう一方の部分空間(to-space)にコピーする。GCが終了するとプログラムは今度はto-spaceを使って動作を継続する。2つの部分空間の役割はGCのたびに入れ替わる。</p>

    <div class="figure"><div class="caption">GC前</div><img src="http://smpl.up.seesaa.net/copying-gc/copying-1.png" alt="Copying GC (前)"/></div>

    <div class="figure"><div class="caption">GC後</div><img src="http://smpl.up.seesaa.net/copying-gc/copying-2.png" alt="Copying GC (後)"/></div>

    <p>まずはmark and sweep方式をおさらい。その後、
copying GCの説明に先立ちmark-compact方式を説明する。</p><a name="more"></a><h4>mark and sweep 方式</h4>

    <p>mark and sweep方式のGCは大略次のような動作をする。</p>

    <div>
<img src="http://smpl.up.seesaa.net/copying-gc/mark-and-sweep-1.png" alt="Mark and Sweep GC (ルートセットからのマーク)" style="float: left; margin-right: 1em; margin-bottom: 1em;"/>
<p>1. グローバル変数やCPUのレジスタ、マシンスタックなどの既知のポインタ(纏めてルートセットという)が指すオブジェクトをマークする。</p>
<br style="clear: both;"/>
</div>

    <div>
<img src="http://smpl.up.seesaa.net/copying-gc/mark-and-sweep-2.png" alt="Mark and Sweep GC (マーク完了)" style="float: left; margin-right: 1em; margin-bottom: 1em;"/>

<p>2. 既にマークされたオブジェクトからポイントされているオブジェクトを再帰的に全てマークする。</p>
<br style="clear: both;"/>
</div>

    <div>    <p>マークされたオブジェクトはまだ使われているオブジェクトなので生き残る。マークされなかったオブジェクトは最早プログラムから参照不可能となったものなので回収される。</p>

<img src="http://smpl.up.seesaa.net/copying-gc/mark-and-sweep-3.png" alt="Mark and Sweep GC (マーク解除・ごみ回収後)" style="float: left; margin-right: 1em; margin-bottom: 1em; "/>

<p>3. ヒープの全オブジェクトを走査。マークされたオブジェクトはマークを解除し、マークされなかったオブジェクトは回収する。回収されたメモリはリストで管理され、次にアロケートする際に再利用される。</p>
<br style="clear: both;"/>
</div>

    <h4>mark and sweep方式の性質</h4>

    <p>マークと、マークの解除及び回収という、2パスの手順からなる。</p>

    <p>オブジェクトが移動されないので、ヒープの断片化という問題が生じる。このため空間利用効率が下がり、局所性も低下する。効率よくメモリを割り当てようとするとアロケータも複雑になる。</p>

    <h4>mark-compact方式</h4>

    <p>copying GCに進む前にmark-compact方式を説明する。その方が格段に理解が容易になる。</p>


    <p>mark and sweep方式ではヒープの断片化が問題となった。
mark-compact方式ではそれを解決するため、
mark and sweep方式と同様に到達可能オブジェクトをマークした後、
それらのオブジェクトを移動させてヒープの隙間を詰める。これをコンパクションという。</p>

    <p>空き領域が一つの大きなかたまりにまとまるため、アロケータは簡単になる。</p>

    <h4>two fingerアルゴリズム</h4>
    <p>ヒープの上の方にいるオブジェクトを空いているところに下から順に詰め込む。教室の後ろの方に座っている学生を一番前から順に座らせるようなものか。</p>

    <ol>
      <li>コンパクション

<p>freeポインタとliveポインタという2つのポインタをヒープの下と上から出発させる。freeポインタは空きスロットを、liveポインタは生き残ったオブジェクトを探す。</p>

<div class="figure"><img src="http://smpl.up.seesaa.net/copying-gc/twofinger-1.png" alt="two fingerアルゴリズム(開始状態)"/></div>
<p>liveポインタが生きているオブジェクトを発見するとそれをfreeポインタが指す空きスロットにコピーする。元の場所にはコピー先を記録する。これをforwarding pointerと呼ぶ。
freeポインタは次の空きスロットに進む。
</p>

<div class="figure"><img src="http://smpl.up.seesaa.net/copying-gc/twofinger-2.png" alt="two fingerアルゴリズム(オブジェクトEを空きスロットへ移動)"/></div>

<p>freeポインタとliveポインタが出会ったらコンパクション完了。</p>

<div class="figure"><img src="http://smpl.up.seesaa.net/copying-gc/twofinger-3.png" alt="two fingerアルゴリズム(完了状態)"/></div>
</li>

      <li>ポインタ修正
<p>次に、オブジェクトの移動前の位置を指していたポインタを移動先を指すように書き換える。
移動したオブジェクトの元の位置に残されているforwarding pointerでポインタを更新する。</p>

<p>ポインタが指していたオブジェクトが移動されたかどうかは、liveポインタと比較して大小をみれば判る。</p>
</li>
    </ol>


    <h4>two fingerアルゴリズムの性質</h4>
    <p>このアルゴリズムはシンプルで速度もそこそこだが、同じ大きさのオブジェクトのみの場合に限定されること、また移動によってオブジェクトの順序がばらばらになり局所性を低下させるといった欠点がある。</p>

    <h4>Lisp2アルゴリズム</h4>

    <p>ヒープの先頭から順に隙間を詰めるように移動させる。最も素朴なコンパクション方法。</p>

    <div class="figure"><img src="http://smpl.up.seesaa.net/copying-gc/compact-lisp2.png" alt="lisp2アルゴリズムによる移動の様子"/></div>

    <ol>
      <li>移動先の計算
<p>オブジェクトを移動させる前に、移動先だけ計算してしまう。</p>
<p>freeポインタとliveポインタの2つのポインタを使う点はtwo fingerアルゴリズムと同じだが、どちらもヒープの下から出発させる。</p>
<p>liveポインタが生きているオブジェクトを発見すると移動先のアドレスを計算してオブジェクトに余分に持たせているforwarding pointerに記録する。
freeポインタもオブジェクトの大きさだけ進める。</p>
</li>
      <li>ポインタの修正
<p>次に生きているオブジェクトを指す全てのポインタをforwarding pointerに従って更新する。</p>
</li>
      <li>コンパクション
<p>最後にオブジェクトを実際に移動させる。</p>
</li></ol>

    <h4>Lisp2アルゴリズムの性質</h4>

    <p>大きさの異なるオブジェクトが混在するヒープを扱えて、オブジェクトの順序も保たれる。two fingerアルゴリズムが持つ問題点を解消している。</p>
    <p>一方、3パス(マークも含めたGC全体では4パス)もかかること、オブジェクトごとにforwarding pointerのための余分なスペースを要するという短所もある。</p>

    <h4>そしてcopying GCへ</h4>

    <p>mark-compact方式を睨んでもう一つ二つ工夫を加えるとcopying GCが出来上がる。</p>

    <!-- mark-compact方式再考 -->

    <p>まずはmark-compact方式のポイントを考えてみよう。</p>
    <ul>
      <li>ポインタ修正のためにオブジェクトのコピー先を示すforwarding pointerを記録する。</li>
      <li>アルゴリズムによってはコンパクションが別のオブジェクトの移動元を上書きしてしまう。</li>
    </ul>

    <p>Lisp2方式でforwarding pointer専用に余分なスペースが必要になったのは、オブジェクトのコピー元が別のオブジェクトのコピーにより上書きされる可能性があり、移動後の残骸にforwarding pointerを記録することができないためだった。</p>
    <p>copying GCではヒープを2つの部分空間に分け、一回のGCではfrom-spaceからto-spaceへのみコピーする。だからコンパクションによる上書きは起こらない。従ってforwarding pointerは移動元にそのまま上書きできる。</p>

 <!-- 2. markの排除 -->

    <p>更に、コンパクションがオブジェクトの探索を兼ねることができれば、マークのフェーズをなくすことができる。</p>

    <p>mark and sweep方式やmark-compact方式では一般にスタックを利用して到達可能なオブジェクトを探索しマークをつける。つまり深さ優先探索する。</p>

    <p>一方、探索はキューを用いた幅優先探索でも行える。
幅優先探索では、キューの先頭からオブジェクトを一つ取り出してそのオブジェクトの持つポインタをチェックする。ポインタが新しいオブジェクトを指していればそれをキューの末尾に追加する。</p>

    <p>copying GCではto-spaceがまさにそのキューの役割を果たす。to-spaceへのコピーはそのままキューへの追加とみることができる。</p>

    <h4>copying GCのアルゴリズム</h4>

    <p>copying GCではfreeポインタとscanポインタの2つのポインタを使う。freeポインタをto-spaceの下から出発させる。移動されるオブジェクトはfreeポインタが指すアドレスにコピーされ、freeポインタがそのオブジェクトの大きさだけ進められる。</p>

    <ol>
      <li><p>まず、ルートセットから指されるオブジェクトをto-spaceに全てコピーする。コピー元にはforwarding pointerを記録する。</p>
<p>それが済んだらscanポインタをto-spaceの下から出発させる。scanポインタは探索キューの先頭を意味する。scanポインタより下は既に処理が済んだもの、scanポインタより上はまだ処理されていないものとなる。</p></li>

      <li><p>scanポインタが指すオブジェクトの持つポインタを調べる。
ポインタが指すオブジェクトがまだ移動されていなければそのオブジェクトをto-spaceにコピーし、forwarding pointerを元の位置に記録する。既に移動済なら記録されているforwarding pointerを使ってポインタの修正だけをする。
全てのポインタを処理したら、scanポインタを進める。</p>

<div class="figure">
<div class="caption">オブジェクトAの処理前</div>
<img src="http://smpl.up.seesaa.net/copying-gc/copying-scan1.png" alt="Copying GC: オブジェクトAのスキャン(前)"/></div>

<div class="figure">
<div class="caption">処理後</div>
<img src="http://smpl.up.seesaa.net/copying-gc/copying-scan2.png" alt="Copying GC: オブジェクトAのスキャン(後)"/></div>

<p>上図ではオブジェクトAは3つのポインタを持ち、それぞれB、C、Dを指している。このうちBは既にto-spaceにコピーされているのでforwarding pointerでポインタの修正だけ行う。C、Dは新たに発見したオブジェクトなのでto-spaceへのコピーとforwarding pointerの記録を伴う。</p></li>
      <li><p>scanポインタがfreeポインタに追い付いたら、to-spaceの全てのオブジェクトが処理済となりGCは完了。コピーされなかったオブジェクトは破棄される。</p></li>
</ol>

    <h4>copying GCの性質</h4>

    <p>オブジェクトの探索・コンパクション・ポインタ修正が全て一回の走査で完了する。これがcopying GCの大きな特長であるように思える。コンパクションを行うのでmark-compact方式と同様のアロケータの単純さなどの長所も維持する。</p>

    <p>短所はなんといってもメモリの半分しかプログラムで利用できない点だろう。ただし、mark and sweep方式(やmark-compact方式)の側にもマークスタックの大きさを定数で押さえにくいという問題がある。マークスタックの深さは最悪の場合、全到達可能オブジェクトの半数程度になるのではなかろうか。</p>
  
    <h4 id="copyinggc-ref">リンク</h4>

    <ul>
      <li id="copyinggc-ref-endo1998"><a href="http://www.is.s.u-tokyo.ac.jp/vu/jugyo/processor/process/soft/compilerresume/gc/gc.html">情報科学実験II資料 一般教養としてのGarbage Collection</a></li>
      <li id="copyinggc-ref-taura1998"><a href="http://www.is.s.u-tokyo.ac.jp/vu/jugyo/processor/process/soft/compilerresume/coverq8/coverq8.html">情報科学実験II資料 (8) Garbage Collection</a>
</li>
   </ul>

<div class="amazlet-box" style="margin-bottom:0px; font-size: 9pt;"><div class="amazlet-image" style="float:left;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://images-jp.amazon.com/images/P/0471941484.09.MZZZZZZZ.jpg" alt="Garbage Collection: Algorithms for Automatic Dynamic Memory Management" style="border: none;" /></a></div><div class="amazlet-info" style="float:left;margin-left:15px;line-height:120%"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank">Garbage Collection: Algorithms for Automatic Dynamic Memory Management</a><div class="amazlet-powered-date" style="font-size:7pt;margin-top:5px;font-family:verdana;line-height:120%">posted with <a href="http://www.amazlet.com/browse/ASIN/0471941484/bisui-1-22" title="Garbage Collection: Algorithms for Automatic Dynamic Memory Management" target="_blank">amazlet</a> on 07.04.04</div></div><div class="amazlet-detail">Richard Jones Rafael Lins <br />John Wiley &amp; Sons Ltd (Import) (1996/07/12)<br />売り上げランキング: 3600<br /></div><div class="amazlet-review" style="margin-top:10px; margin-bottom:10px"><div class="amazlet-review-average" style="margin-bottom:5px">おすすめ度の平均: <img src="http://images-jp.amazon.com/images/G/09/x-locale/common/customer-reviews/stars-5-0.gif" alt="5.0" /></div><img src="http://images-jp.amazon.com/images/G/09/x-locale/common/customer-reviews/stars-5-0.gif" alt="5" /> よくまとまっていて分かりやすい<br /></div><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/0471941484/bisui-1-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jp で詳細を見る</a></div></div><div class="amazlet-footer" style="clear: left"></div></div>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/29800843.html">
<link>http://smpl.seesaa.net/article/29800843.html</link>
<title>Common Lisp: loopマクロ用法抄</title>
<description>GrahamのANSI Common Lispでは嫌われていて碌に説明のないloopマクロ。一方、Practical Common Lispでは対照的に好んで用いられていて、全編に渡って頻繁に使われている。しかしloopマクロは難しいという意識があるのかその説明は第22章とかなり後回しにされており、ちぐはぐな感を受ける。ここでは、LOOP for Black-Belts という題のつけられたその章で解説されているloopマクロの用法を整理してみた。    ANSI Commo...</description>
<dc:subject>雑記</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2006-12-17T14:22:44+09:00</dc:date>
<content:encoded><![CDATA[
<p>Grahamの<cite>ANSI Common Lisp</cite>では嫌われていて碌に説明のないloopマクロ。一方、<cite>Practical Common Lisp</cite>では対照的に好んで用いられていて、全編に渡って頻繁に使われている。しかしloopマクロは難しいという意識があるのかその説明は第22章とかなり後回しにされており、ちぐはぐな感を受ける。ここでは、LOOP for Black-Belts という題のつけられたその章で解説されているloopマクロの用法を整理してみた。</p>
    <p><cite>ANSI Common Lisp</cite>での黒魔術扱いに敬遠していたloopマクロだったが、こうして整理してみるとそれほど難しく考えずとも便利に使うことができそうだ。</p><a name="more"></a><div style="float:right; border:0.5em;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/1590592395/bisui-1-22"><img style="border:0px" src="http://rcm-images.amazon.com/images/P/1590592395.01._SL100_SCTZZZZZZZ_.jpg" alt="Practical Common Lisp"/></a></div>

    <h4>目次</h4>

    <ul>
      <li>繰り返し
<ul>
	  <li><a href="#cl-loop-counting-iteration">計数繰り返し</a></li>
	  <li><a href="#cl-loop-iteration-over-collection">コレクション内繰り返し</a></li>
	  <li><a href="#cl-loop-equals-then-iteration">変数更新しながら繰り返し</a></li>
	  <li><a href="#cl-loop-additional-termination-test">ループ終了条件の追加</a></li>
	</ul>
</li>
      <li>アクション
<ul>
	  <li><a href="#cl-loop-accumulation">集約</a></li>
	  <li><a href="#cl-loop-local-variables">ループ内ローカル変数</a></li>
	  <li><a href="#cl-loop-execution">任意の式の実行</a></li>
	  <li><a href="#cl-loop-conditional">条件分岐</a></li>
	  <li><a href="#cl-loop-return">ループ中断</a></li>
	</ul></li>

      <li>その他
<ul>
	  <li><a href="#cl-loop-prologue-epilogue">初期化・後始末</a></li>
	  <li><a href="#cl-loop-invariant">不変条件</a></li>
	  <li><a href="#cl-loop-destructuring-bind">分割代入</a></li>
	</ul>
</li>
      <li><a href="#cl-loop-references">参考</a></li>
    </ul>

    <h4 id="cl-loop-intro">概要</h4>

    <p>一つ以上の繰り返し(<code>for</code>節)の後に、ループの中身であるアクションを一つ以上書く。アクションの主たるものは値の集約だが、任意の式の実行も可能。</p>
    <p>繰り返しは複数個指定でき、その場合は入れ子ではなく同じループの中で並行して扱われる。</p>
    <p>繰り返しの終了条件のどれかが満たされるとループは終了する。</p>
    <p>アクションも複数指定でき、それらは毎回順番に実行される。</p>

    <p>例: この繰り返しではxとyがそれぞれ並行して進む。先にxのリストが尽きるため、yはまだ残りがあるがループは3回で終了する。</p>
    <pre class="example-code">(loop for x in (1 3 5)
      for y in (2 4 6 8 10)
      collect (cons x y))
==&gt; ((1 . 2) (3 . 4) (5 . 6))
</pre>

    <p>例: 一つのループで複数のアクション。一つめでyを更新し、二つめでloopの戻り値となる値に集約。</p>
    <pre class="example-code">(loop for x in '(1 2 3 4 5)
      sum x into y
      collect y)
==&gt; (1 3 6 10 15)</pre>

    <h4 id="cl-loop-counting-iteration">計数繰り返し</h4>

    <table class="cl-loop-synopsis">
      <tr>
	<td><code>for</code> <var>変数</var>&nbsp;</td>
	<td>[</td>
	<td>
<table class="cl-loop-synopsis-select">
	    <tr><td><code>from</code></td>     <td><var>N</var></td></tr>
	    <tr><td><code>upfrom</code></td>   <td><var>N</var></td></tr>
	    <tr><td><code>downfrom</code>&nbsp;</td> <td><var>N</var></td></tr>
	  </table></td>
	<td>]&nbsp;</td>
	<td>[</td>
	<td>
<table class="cl-loop-synopsis-select">
	    <tr><td><code>to</code></td>     <td><var>N</var></td></tr>
	    <tr><td><code>upto</code></td>   <td><var>N</var></td></tr>
	    <tr><td><code>downto</code>&nbsp;</td> <td><var>N</var></td></tr>
	    <tr><td><code>below</code></td>  <td><var>N</var></td></tr>
	    <tr><td><code>above</code></td>  <td><var>N</var></td></tr>
	  </table></td>
	<td>]&nbsp;</td>
	<td><code>[ by <var>N</var> ] ～</code></td>
      </tr>
    </table>

    <!-- 
    <pre class="synopsis">for <var>変数</var> [<span class="select">from     <var>N</var></span>] [<span class="select">to     <var>N</var></span>] [by <var>N</var>] ～
          <span class="select">upfrom   <var>N</var></span>   <span class="select">upto   <var>N</var></span>
          <span class="select">downfrom <var>N</var></span>   <span class="select">downto <var>N</var></span>
                         <span class="select">below  <var>N</var></span>
                         <span class="select">above  <var>N</var></span>
</pre>
 -->
    <p>次の3要素の1つ以上が必要:</p>
    <dl>
      <dt><code>from</code> <var>数値</var> (始値の指定)</dt>
      <dd><code>from <var>N</var></code> / <code>upfrom <var>N</var></code> / <code>downfrom <var>N</var></code> (デフォルトは0)</dd>
      <dt><code>to</code> <var>数値</var> (上限または下限の指定)</dt>
      <dd><code>to</code> <var>N</var> / <code>upto</code> <var>N</var> / <code>downto</code> <var>N</var> / <code>below</code> <var>N</var> / <code>above</code> <var>N</var></dd>
      <dt><code>by <var>数値</var></code> (増分の指定)</dt>
      <dd><code>by <var>N</var></code> (デフォルトは1または-1)</dd>
    </dl>

    <p>デクリメント時の注意点:</p>

    <ul>
      <li>始値のデフォルトは存在しないため省略不可。</li>
      <li><code>downfrom</code>か<code>downto</code>/<code>below</code>によって増分の方向が負であることを明示する必要あり。(例えば、<code>from 20 to 10</code>等は不可。<code>downfrom 20 to 10</code>や<code>from 20 downto 10</code>等とする)</li>
</ul>
    <p>例:</p>
    <pre class="example-code">(loop for i from 10 to 50 by 5 collect i)
==&gt; (10 15 20 25 30 35 40 45 50)
</pre>

    <table class="cl-loop-synopsis">
      <tr><td><code>repeat</code> <var>N</var> ～</td></tr>
    </table>

    <!-- 
    <pre class="synopsis">repeat <var>N</var> ～
</pre>
 -->
    <p><var>N</var>回繰り返し。カウンタ変数の値そのものは不要な時に。</p>

    <h4 id="cl-loop-iteration-over-collection">コレクション内繰り返し</h4>

    <table class="cl-loop-synopsis">
      <tr><td><code>for</code> <var>変数</var>&nbsp;</td>
	<td><table class="cl-loop-synopsis-select">
	    <tr><td><code>in</code></td>    <td><var>リスト</var></td></tr>
	    <tr><td><code>on</code></td>    <td><var>リスト</var></td></tr>
	    <tr><td><code>across</code></td><td><var>ベクタ</var></td></tr>
	  </table>
</td>
	<td>&nbsp;[ <code>by</code> <var>ステップ関数</var> ] ～</td>
      </tr>
    </table>
    <!-- 
    <pre class="synopsis">for <var>変数</var> <span class="select">in     <var>リスト</var></span> [by <var>ステップ関数</var>] ～
         <span class="select">on     <var>リスト</var></span>
         <span class="select">across <var>ベクタ</var></span>
</pre>
 -->
    <p>ステップ関数は次の要素を保持する位置を得る関数で、
デフォルトは当然<code>#'cdr</code>。この関数次第でリストに限らず広く応用もできそうだ。</p>
    <p>「<code>on</code> <var>リスト</var>」の場合、変数には要素ではなくconsセルが代入される。</p>

    <pre class="example-code">(loop for x in '(1 2 3 4) collect x) ==&gt; (1 2 3 4)
(loop for x in '(1 2 3 4) by #'cddr collect x) ==&gt; (1 3) ; 一つ飛ばし
(loop for x on '(1 2 3) collect x) ==&gt; ((1 2 3) (2 3) (3))
(loop for x on '(1 2 3 4 5) by #'cddr collect x)
==&gt; ((1 2 3 4 5) (3 4 5) (5))
</pre>

    <table class="cl-loop-synopsis">
      <tr><td><code>for</code> <var>変数</var> <code>being the</code>&nbsp;</td>
	<td><table class="cl-loop-synopsis-select">
	    <tr><td><code>hash-keys</code></td></tr>
	    <tr><td><code>hash-values</code></td></tr>
	  </table></td>
	<td>&nbsp;<code>in</code> <var>ハッシュ表</var> [ <code>using (</code></td>
	<td><table class="cl-loop-synopsis-select">
	    <tr><td><code>hash-value</code></td></tr>
	    <tr><td><code>hash-key</code></td></tr>
	  </table>
</td>
	<td>&nbsp;<var>変数</var> <code>)</code> ] ～</td>
      </tr>
    </table>

    <table class="cl-loop-synopsis">
      <tr><td><code>for</code> <var>変数</var> <code>being the</code>&nbsp;</td>
	<td><table class="cl-loop-synopsis-select">
	    <tr><td><code>symbols</code></td></tr>
	    <tr><td><code>present-symbols</code></td></tr>
	    <tr><td><code>external-symbols</code></td></tr>
	  </table></td>
	<td>&nbsp;<code>in</code> <var>パッケージ</var> ～</td>
      </tr>
    </table>

    <!-- 

    <pre class="synopsis">for <var>変数</var> being the <span class="select">hash-keys  </span> in <var>ハッシュ表</var> [using (<span class="select">hash-value</span> <var>変数</var>)] ～
                   <span class="select">hash-values</span>                       <span class="select">hash-key  </span>

for <var>変数</var> being the <span class="select">symbols         </span> in <var>パッケージ</var> ～
                   <span class="select">present-symbols </span>
                   <span class="select">external-symbols</span>
</pre>
 -->
    <p>ハッシュ表やパッケージの要素についての繰り返し。ハッシュ表ではループ変数はキーか値の一方について繰り返すが<code>using</code>を使うことで他方も参照可能。</p>
    <pre class="example-code">(defvar ht (make-hash-table))
(setf (gethash 'foo ht) 1)
(setf (gethash 'bar ht) 2)
(loop for x being the hash-keys in ht collect x) ==&gt; (BAR FOO)
(loop for x being the hash-keys in ht using (hash-value y) collect (cons x y))
==&gt; ((BAR . 2) (FOO . 1))</pre>


    <h4 id="cl-loop-equals-then-iteration">変数更新しながら繰り返し</h4>

    <table class="cl-loop-synopsis">
      <tr><td><code>for</code> <var>変数</var> <code>=</code> <var>式1</var> [ <code>then</code> <var>式2</var> ] ～</td></tr>
    </table>
    <!-- 
    <pre class="synopsis">for <var>変数</var> = <var>式1</var> [ then <var>式2</var> ] ～
</pre>
 -->
    <p>最初のループでは式1を評価した結果を変数の値とし、以降のループでは式2(省略された場合は式1)を評価した値で更新する。<code>do</code>マクロとは異なり、式2が省略された場合も式1を毎回評価して変数を更新する。</p>
    <p>複数の変数を更新する場合、<code>for</code>を複数並べるか、<code>and</code>を使う。</p>

    <pre class="example-code">(loop repeat 5
      for x = 0 then y	        ← 前回のyが使われる
      for y = 1 then (+ x y)    ← 新しいxが使われる
      ～)

(loop repeat 5
      for x = 0 then y          ← 前回のyが使われる
      and y = 1 then (+ x y)    ← 前回のxが使われる
      ～)
</pre>

    <h4 id="cl-loop-additional-termination-test">ループ終了条件の追加</h4>

    <table class="cl-loop-synopsis">
      <tr><td><table class="cl-loop-synopsis-select">
	    <tr><td><code>while</code></td></tr>
	    <tr><td><code>until</code></td></tr>
	  </table>
</td>
	<td>&nbsp;<var>式</var> ～</td></tr>
    </table>

    <!-- 
    <pre class="synopsis"><span class="select">while</span> 式 ～
<span class="select">until</span>
</pre>
 -->
    <p><var>式</var>が偽になったら(<code>until</code>の場合は真になったら)ループは終了。</p>

    <pre class="example-code">(loop for y = nil then (cons 'x y) while (&#60; (length y) 3) collect y)
==&gt; (NIL (X) (X X))</pre>

    <hr/>

    <h4 id="cl-loop-accumulation">集約</h4>
    <table class="cl-loop-synopsis">
      <tr><td><table class="cl-loop-synopsis-select">
	    <tr><td><code>collect </code></td></tr>
	    <tr><td><code>append  </code></td></tr>
	    <tr><td><code>nconc   </code></td></tr>
	    <tr><td><code>count   </code></td></tr>
	    <tr><td><code>sum     </code></td></tr>
	    <tr><td><code>maximize</code></td></tr>
	    <tr><td><code>minimize</code></td></tr>
	  </table>
</td>
	<td>&nbsp;<var>式</var> [ <code>into</code> <var>変数</var> ]</td></tr>
    </table>
    <!-- 
    <pre class="synopsis"><span class="select">collect </span> <var>式</var> [into <var>変数</var>]
<span class="select">append  </span>
<span class="select">nconc   </span>
<span class="select">count   </span>
<span class="select">sum     </span>
<span class="select">maximize</span>
<span class="select">minimize</span>
</pre>
     -->
    <p>式を評価しその結果をそれぞれの方法で集約する。</p>
    <p><code>into</code>句があると、集約の結果はループ内ローカルな変数に代入される。ない場合は集約の結果は<code>loop</code>のデフォルトの戻り値となる。</p>
    <p>動詞はそれぞれ<em>～ing</em>という動名詞形でもよい。</p>
    
    <ul>
<li><code>collect</code>:  式を並べたリストを作る</li>
<li><code>append</code>:	  式をリストとみて、それらを結合したリストを作る</li>
<li><code>nconc</code>:	  〃</li>
<li><code>count</code>:	  式が真となる場合の回数を数える</li>
<li><code>sum</code>:	  式を足し合わせる</li>
<li><code>maximize</code>: 最大値を選ぶ</li>
<li><code>minimize</code>: 最小値を選ぶ</li>
</ul>

    <h4 id="cl-loop-local-variables">ループ内ローカル変数</h4>

    <table class="cl-loop-synopsis">
      <tr><td><code>with</code> <var>変数</var> [ <code>=</code> <var>式</var> ]</td></tr>
    </table>
    <!-- 
    <pre class="synopsis">with <var>変数</var> [= <var>式</var>]</pre>
 -->
    <h4 id="cl-loop-execution">任意の式の実行</h4>

    <table class="cl-loop-synopsis">
      <tr><td><code>do</code> <var>式</var> [ <var>式</var> ... ]</td></tr>
    </table>
    <!-- 
    <pre class="synopsis">do <var>式</var> [<var>式</var> ...]
</pre> -->
    <p>任意のLisp式を毎回実行。別のloopマクロ内キーワードが出現するかloopマクロの終わりまで任意個のLisp式を続けられる。</p>

    <h4 id="cl-loop-conditional">条件分岐</h4>

    <table class="cl-loop-synopsis">
      <tr><td><table class="cl-loop-synopsis-select">
	    <tr><td><code>if    </code></td></tr>
	    <tr><td><code>when  </code></td></tr>
	    <tr><td><code>unless</code></td></tr>
	  </table>
</td>
	<td>&nbsp;<var>条件</var> ～ [ <code>else</code> … ] [ <code>end</code> ]</td></tr>
    </table>
    <!-- 
    <pre class="synopsis"><span class="select">if    </span> <var>条件</var> ～ [else …] [end]
<span class="select">when  </span>
<span class="select">unless</span>
</pre> -->

    <p>条件は任意のLisp式。<code>if</code>と<code>when</code>は同義。</p>
    <p>～や…の部分はloopマクロの節。複数の節を入れたい場合はそれらを<code>and</code>で繋ぐ。</p>
    <p><code>end</code>は省略可。</p>

    <pre class="example-code">(loop for x from 1 to 5 if (evenp x) collect x) ==&gt; (2 4)
</pre>

    <h4 id="cl-loop-return">ループ中断</h4>

    <table class="cl-loop-synopsis">
      <tr><td><code>return</code> <var>式</var></td></tr>
    </table>
    <table class="cl-loop-synopsis">
      <tr><td><code>named</code> <var>ラベル</var> <code>for</code> ...</td></tr>
    </table>

    <!-- 
    <pre class="synopsis">return <var>式</var>

named <var>ラベル</var> for …
</pre>
 -->
    <p><code>return</code>はループを中断する。<code>finally</code>節は実行されない。ループから脱出したいが正常終了と同様に<code>finally</code>節を実行したり値を返したい場合、<code>do</code>節の中で<code>LOOP-FINISH</code>マクロを用いる。</p>
    <p><code>named</code>はループが作るブロックに名前をつける。その名前を使って<code>RETURN-FROM</code>してもループを抜けられる。</p>

    <pre class="example-code">(loop named outer for xs in lists do
      (loop for x in xs do
            (if (foop x) (return-from outer x) ...)))
</pre>

    <hr/>

    <h4 id="cl-loop-prologue-epilogue">繰り返しの初期化・後始末</h4>

    <table class="cl-loop-synopsis">
      <tr><td><table class="cl-loop-synopsis-select">
	    <tr><td><code>initially</code></td></tr>
	    <tr><td><code>finally  </code></td></tr>
	  </table>
</td>
	<td>&nbsp;<var>式</var> [ <var>式</var> ... ]</td></tr>
    </table>
    <!-- 
    <pre class="synopsis"><span class="select">initially</span> <var>式</var> [<var>式</var> ...]
<span class="select">finally  </span>
</pre>
 -->
    <p>どちらもdo同様に次のloopキーワードまで任意個の式が書ける。</p>

    <p><code>finally</code>節が実行されない3条件:</p>

    <ul>
      <li><code>return</code>節で脱出した場合</li>
      <li><code>RETURN</code>, <code>RETURN-FROM</code>で脱出した場合
(つまり、<code>finally</code>は <code>unwind-protect</code> で実現されるわけではない)</li>
      <li>ループが <code>always</code>, <code>never</code>, <code>thereis</code> 条件で終了した場合</li>
</ul>

    <h4 id="cl-loop-invariant">不変条件</h4>

    <table class="cl-loop-synopsis">
      <tr><td><table class="cl-loop-synopsis-select">
	    <tr><td><code>always </code></td></tr>
	    <tr><td><code>never  </code></td></tr>
	    <tr><td><code>thereis</code></td></tr>
	  </table>
</td>
	<td>&nbsp;<var>式</var> ～</td></tr>
    </table>
    <!-- 
    <pre class="synopsis"><span class="select">always </span> <var>式</var> ～
<span class="select">never  </span>
<span class="select">thereis</span>
</pre>
 -->
    <p>ループの全繰り返しに渡って不変条件が守られたかをloopの戻り値とする。条件が破られるとループを中断、<code>finally</code>によるエピローグもスキップする。</p>

    <table>
      <thead>
<tr><th>       </th><th>中断する条件</th><th>中断時の戻り値</th><th>完了時の戻り値</th></tr>
</thead>
      <tbody>
	<tr><td><code>always</code> </td><td>式が偽になったら中断	 </td><td>nil	      </td><td>t	      </td></tr>
      <tr><td><code>never</code>  </td><td>式が真になったら中断	 </td><td>nil	      </td><td>t	      </td></tr>
      <tr><td><code>thereis</code></td><td>式がnil以外になったら中断</td><td>式の値	      </td><td>nil           </td></tr>
</tbody></table>

    <h4 id="cl-loop-destructuring-bind">分割代入</h4>

    <p>記述力は<code>destructuring-bind</code>に劣るものの、<code>for</code>や<code>with</code>による変数への代入は、リストやconsの分割代入が可能。</p>
    <p>値を無視したい部分にはnilを使う。</p>

    <pre class="example-code">for (a b) in '((1 2) (3 4) (5 6)) ～
for (kar . kdr) on '((1 . 2) (3 . 4) (5 . 6)) ～
</pre>

    <hr/>

    <div style="float:right; border: 0.5em;"><iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=bisui-1-22&amp;o=9&amp;p=8&amp;l=as1&amp;asins=1590592395&amp;fc1=3A3A3A&amp;IS2=1&amp;lt1=_top&amp;lc1=FF5A13&amp;bc1=FFFFFF&amp;bg1=FFFFFF&amp;f=ifr" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe></div>

    <h4 id="cl-loop-references">参考</h4>

    <ul>
      <li>Peter Seibel, <a href="http://www.amazon.co.jp/gp/product/1590592395?ie=UTF8&amp;tag=bisui-1-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=1590592395"><cite>Practical Common Lisp</cite></a>
 / <a href="http://www.gigamonkeys.com/book/">著者のサイト(オンライン版有)</a>
</li>
      <li>Paul Graham, <a href="http://www.amazon.co.jp/gp/product/4894714337?ie=UTF8&amp;tag=bisui-1-22&amp;linkCode=as2&amp;camp=247&amp;creative=1211&amp;creativeASIN=4894714337"><cite>ANSI Common Lisp</cite></a></li>
    </ul>
    <div style="clear:both"></div>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/25482628.html">
<link>http://smpl.seesaa.net/article/25482628.html</link>
<title>IPAフォントの0x5cをバックスラッシュに</title>
<description>TrueTypeフォントの0x5cを円記号からバックスラッシュに書き換える方法を紹介。IPAフォントに直接適用できるパッチと、TrueTypeフォント一般(ただし*.ttfのみ)を書き換えるスクリプトを提供。試しにGNOMEを導入したのでGNOME Terminalを使い始めた。ところがその中でIPAフォントを使ってみたら、0x5cが円記号で表示されるのが気になって仕方がない。そこで0x5cのグリフがバックスラッシュになるよう、fontforgeで編集した。</description>
<dc:subject>日記</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2006-10-14T22:42:53+09:00</dc:date>
<content:encoded><![CDATA[
<p>TrueTypeフォントの0x5cを円記号からバックスラッシュに書き換える方法を紹介。IPAフォントに直接適用できるパッチと、TrueTypeフォント一般(ただし*.ttfのみ)を書き換えるスクリプトを提供。</p>

<p>試しにGNOMEを導入したのでGNOME Terminalを使い始めた。
ところがその中でIPAフォントを使ってみたら、0x5cが
円記号で表示されるのが気になって仕方がない。</p>
<p>そこで0x5cのグリフがバックスラッシュになるよう、<a href="http://fontforge.sourceforge.net/">fontforge</a>で編集した。</p><a name="more"></a><p>手順は次の通り。</p>
<ol>
<li>0x5c (REVERSE SOLIDUS)に入っている円記号にしか見えないグリフを、0xa5 (YEN SIGN)にコピー。IPAフォントではここにはグリフが設定されていない。</li>
<li>スラッシュ、即ち0x2f (SOLIDUS)のグリフを左右反転させて0x5cのグリフを作成。</li>
</ol>
<p>IPAフォントへのパッチを作成した。bspatchで例えば次のようにして適用する。このコマンドは生成するファイル名を第2引数にとるのが少し変わっている。</p>
<pre>% bspatch /path/to/ipag.ttf new.ipag.ttf ipag.ttf.patch</pre>

<p>フォントファイルに対して上記の操作を行うスクリプトも作成した。実行にはfontforgeが必要。バックスラッシュをスラッシュの左右反転で作るので、それでも困らないフォント限定。*.ttf専用で、今のところ*.ttcには対応させていない。次のように実行する。</p>
<pre>% ./0x5c-reverse-solidus.pe font.ttf new-font.ttf</pre>
<ul>
<li><a href="http://smpl.up.seesaa.net/fonts/0x5c-reverse-solidus.pe">上記の操作を自動で行うスクリプト (実行にはfontforgeが必要)</a></li>
<li><a href="http://smpl.up.seesaa.net/fonts/ipa-fonts-0x5c-patches.tar.gz">IPAフォントを変更するパッチ (bsptachで適用のこと)</a></li>
</ul>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/24918184.html">
<link>http://smpl.seesaa.net/article/24918184.html</link>
<title>語呂合わせ</title>
<description>原口證さんという方が10万桁もの円周率の暗唱に成功とのニュース。    日本語には語呂合わせというものがあるので、一見無意味な数字の並びを暗唱するのに大きな助けになる。円周率の語呂合わせとしては次のものやこれに類似したものがおそらく最もよく知られていよう。              産医師異国に向かう3.14159265      産後厄無く358979      産婦御社(みやしろ)に3238462      虫さんさん闇に鳴く643383279            しか...</description>
<dc:subject>日記</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2006-10-05T07:27:36+09:00</dc:date>
<content:encoded><![CDATA[
<p><a href="http://www.yomiuri.co.jp/national/news/20061004i402.htm">原口證さんという方が10万桁もの円周率の暗唱に成功</a>とのニュース。</p>

    <p>日本語には語呂合わせというものがあるので、一見無意味な数字の並びを暗唱するのに大きな助けになる。円周率の語呂合わせとしては次のものやこれに類似したものがおそらく最もよく知られていよう。</p>

    <blockquote>
    <table>
      <tr><td>産医師異国に向かう</td><td>3.14159265</td></tr>
      <tr><td>産後厄無く</td><td>358979</td></tr>
      <tr><td>産婦御社(みやしろ)に</td><td>3238462</td></tr>
      <tr><td>虫さんさん闇に鳴く</td><td>643383279</td></tr>
    </table>
    </blockquote>
    <p>しかし英語では日本語のようにはいかない。ではどうするのかというと、単語の文字数を数字に当てはめるのだそうだ。多数作られているが、例えばこういうものがある。</p>

    <blockquote>
      <p>How I want a drink, alcoholic of course, after the heavy lectures involving quantum mechanics!</p></blockquote>

    <p>1単語1数字というのも効率が悪そうだが、それぞれの単語から数字を出すのも大変そうだ。それに対して日本語は有り難い。尤も、同じ数字に対する候補が多いので考え出しやすいという一面はありそうだから、悪いことばかりではない。</p>

    <p>英語での語呂合わせ的な記憶法の中で一般的なのは、何かの並びを憶えるためにそれぞれの頭文字が同じ単語で文を作るものらしい。例えば、太陽系の惑星の場合は
</p>
    <blockquote><p>My Very Educated Mother Just Showed Us Nine Planets</p></blockquote>
<p>などがあるそうだ(惑星の英語名は水星から順に、Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto &mdash; Plutoは最近格下げされたが)。</p>

    <p>Wikipediaの<a href="http://en.wikiquote.org/wiki/English_mnemonics">English mnemonics</a>の項では他にも色々なものが紹介されていて面白い。計算機関係ではOSI参照モデル(Physical layer, Data link layer, Network layer, Transport layer, Session layer, Presentation layer, Application layer)の憶え方というのもある。一例を挙げると</p>

    <blockquote>
      <p>Please Do Not Tell Sales People Anything</p></blockquote>
    <p>または上位層から逆順に</p>
    <blockquote>
    <p>All People Say They Never Download Porn</p></blockquote>
    <p>もう傑作である。</p>

    <p>このページと、同じくWikipediaの日本語の<a href="http://ja.wikipedia.org/wiki/%E8%AA%9E%E5%91%82%E5%90%88%E3%82%8F%E3%81%9B">語呂合わせ</a>の項を比較すると違いが際立つ。英語では様々な対象に対して憶え方が作られているのに対し、日本語では数字の語呂合わせには熱心だが、それ以外の対象にはあまり積極的に行われないように思える。その理由をあれこれ考えてみると面白いが、一つには、憶え方を工夫する必要などなさそうなPCMCIAについてまで作られているのを見ると、日本語の駄洒落に似た言葉遊びを楽しむ面も大きいのかも知れない。</p><a name="more"></a>
]]><![CDATA[
]]></content:encoded>
</item>
<item rdf:about="http://smpl.seesaa.net/article/19591451.html">
<link>http://smpl.seesaa.net/article/19591451.html</link>
<title>JavaScript</title>
<description>今更の感も強いが、これまで見様見真似でしか書いたことのなかったJavaScriptを勉強した。    教科書はO'reillyのJavaScript: The Definitive Guide, 4th Edition, David Flanagan。日本語訳は第3版がJavaScript第3版として出ているが、第4版は出ていないようだ。    web上のJavaScriptの紹介にはHTML上の効果としての使い方しか載せていないもの、入門と銘打っていても言語の表層的なシンタ...</description>
<dc:subject>日記</dc:subject>
<dc:creator>猫の手(仮)</dc:creator>
<dc:date>2006-06-21T08:02:35+09:00</dc:date>
<content:encoded><![CDATA[
<p>今更の感も強いが、これまで見様見真似でしか書いたことのなかったJavaScriptを勉強した。</p>
    <p>教科書はO'reillyの
<a title="アマゾンにとぶ" href="http://www.amazon.co.jp/exec/obidos/redirect?link_code=ur2&amp;tag=bisui-1-22&amp;camp=247&amp;creative=1211&amp;path=ASIN%2F0596000480"><!-- http://www.amazon.co.jp/exec/obidos/ASIN/0596000480/ --><cite>JavaScript: The Definitive Guide, 4th Edition</cite>, David Flanagan</a>。
日本語訳は第3版が<a title="アマゾンにとぶ" href="http://www.amazon.co.jp/exec/obidos/redirect?link_code=ur2&amp;tag=bisui-1-22&amp;camp=247&amp;creative=1211&amp;path=ASIN%2F4873110270"><cite>JavaScript第3版</cite></a>として出ているが、第4版は出ていないようだ。</p>
    <p>web上のJavaScriptの紹介にはHTML上の効果としての使い方しか載せていないもの、入門と銘打っていても言語の表層的なシンタックスしか解説していないものがほとんどで、言語として理解しようとすると物足りない。</p>
    <p>一方、この本では前半分を使って言語としてのJavaScriptを丁寧に説明している。特に他のプログラミング言語の知識がある人には非常にわかりやすく、面白い。後半はJavaScriptの主な用途であるウェブブラウザでHTMLと組み合わせての使用のため、そこで登場するJavaScriptオブジェクトを説明するとともに、合わせて必要となるHTMLフォームやCSS、クッキー等の知識も要領よく解説している。</p>
    <p>言語としてのJavaScriptは結構面白く、一読してもっと早くこの本を読んでおけばよかったと感じた。ただ、多くの項目でブラウザやJavaScriptのバージョンによって色々事情が異なるらしく、互換性についての記述が細かい。そのため言語を使おうとする側としては面倒な感を受けるが、多様なブラウザが使われている現状では致し方ないだろう。もっとも、記述があることは解説書としてそういった差異を丁寧にフォローしていると言える。</p>
    <p>そのJavaScriptという言語であるが、JavaScriptは演算子や制御構文といった基本的なシンタックスはCやJavaに似ている。コメントの構文もC/Java譲り。
しかし、似ているのはそれだけ。言語としてはむしろScheme等に近く、そこにプロトタイプベースのオブジェクトを持ち込んだ感じである。</p>
    <p>まず、変数には型がない。値は数値(整数と浮動小数点数の区別はなく、64bit浮動小数点数で全てまかなう)、ブーリアン、文字列、そしてオブジェクトのみ。文字列はimmutable。これらの間で必要に応じて暗黙の型変換があり、便利でもあるが嫌らしいような気もする。
</p>
    <p>クロージャがある(クロージャはオブジェクトの特殊なサブタイプとして扱われる)。
関数定義はクロージャの変数への束縛にすぎない。次の2つはほぼ同義。</p>
    <pre>function foo(x) { return x }
var foo = function(x) { return x }</pre>
    <p>括弧をつけて<code>foo(1)</code>とすれば関数適用だが、<code>foo</code>と名前だけ書くと変数として参照する。</p>
    <p>クロージャであるから当然次のようなコードも可能。</p>
    <pre>function make_counter() {
  var x=0
  return function() { return x++; }
}
var c = make_counter()
alert(c()) // 0を表示
alert(c()) // 1を表示</pre>
    <p>以上に加えて、オブジェクトがある。JavaScriptでいうところのオブジェクトは畢竟、文字列をキーとする連想配列だ。オブジェクトの要素をプロパティといい、通常<code>obj.prop</code>として参照するが、<code>obj["prop"]</code>としても参照できる。連想配列なので、他の言語のハッシュテーブルにも似た</p>
    <pre>var p = { x: 10, y: 20 }</pre>
    <p>というリテラル表現でも書けてしまう。</p>
    <p>オブジェクトのメソッドは、単にプロパティにクロージャが代入されているものだ。</p>
    <pre>obj.method = function () { ... }</pre>
    <p>後ろに関数適用の括弧をつけて<code>obj.method()</code>とすればメソッド呼び出しとなる。
ただし、JavaScriptの関数では<code>this</code>という暗黙的に用意される参照があり、<code>obj.method()</code>という形式で呼ぶとその関数の中で<code>this</code>がレシーバ<code>obj</code>を指す。ちなみに通常の関数呼び出しでも<code>this</code>はあり、グローバル環境の実行コンテクストを指す。</p>
    <p>オブジェクトの作成は<code>new</code>演算子をつけて関数を呼ぶことで行う。<code>new Constructor()</code>とすると、新しい空のオブジェクトが作成され、関数<code>Constructor</code>が呼ばれる。関数<code>Constructor</code>の中ではそれを<code>this</code>として参照できる。オブジェクトを作るための関数(これをコンストラクタと呼ぶ)は、この新しいオブジェクトに必要なプロパティやメソッドを与えることでオブジェクトを「作る」。例えばこんな具合。</p>
    <pre>function Point(x,y) { this.x = x; this.y = y }
var p = new Point(10,20)</pre>
    <p>JavaScriptにはクラスという概念はないが、あるコンストラクタが作成するオブジェクトに共通のプロパティを集めたオブジェクト(これをプロトタイプと呼ぶ)を持てる。オブジェクトは、プロトタイプが持つプロパティを自分自身が備えているかのように振舞う。</p>
    <pre>function Circle(r) { this.r = r }
    // コンストラクタのprototypeプロパティがプロトタイプを指す。
    // デフォルトではプロトタイプは空のオブジェクト。
    // そこにメソッドを追加する。
Circle.prototype.area = function() { return 3.141592 * this.r * this.r }
var c = new Circle(5)
alert(c.area()) // 78.5398と表示</pre>
    <p>他にもあるが、JavaScriptはどういう言語かというと概略こんな