2010年12月26日日曜日

UNIX 6th Editionにシステムコールを追加してみる

という事で、カーネル/VM Advent Calendarの担当日なので、表題の件で書いてみようと思います。カーネル修行中の身という事で、Lions Commentaryで勉強中のUNIX 6th Editionをちょっとだけhackしてみます。

目標
Linuxなどで「man select」としてみると「The select() function call appeared in 4.2BSD.」などと書かれています。こいつを6th Editionに実装してみましょう。
Ancient UNIXではブロックする時には原因となる変数のポインタを引数としてsleep()を呼び出します。逆にブロック要因が解けた時、同じく変数のポインタを引数としてwakeup()を呼び出します。この時、全プロセスの情報をなめ、同じポインタ値を要因としてブロックしている全プロセスを起こすという古典的な手法をとっています。要因が1つしか持てず、リストも1つだけで管理されている事からも、select()が存在しない、というのは納得な話に思えます(と、言いつつselectの実装は理解していないのですが)。
そこで、完璧なものは難しいですが、pipeを多重化して入力待ちするのに必要最低限な機能を持たせた限定版システムコールselectを追加してみる事にします。

アセンブラからシステムコールを呼び出してみる
追加したシステムコールを呼び出すにはアセンブラを書く必要があるため、まずはシステムコールを呼び出すアセンブラの確認です。適当な作業領域に移りましょう。
% ed hello.s
?
a
main:

        mov $1,r0
        sys write; hello; 6
        sys exit
hello:
        <hello\n>

.
w
66
q
% as hello.s
% a.out
hello
ユーザ入力を赤、または青字、ソフト側からの出力を黒字で分けてあります。
ユーザ入力については、まとまったソース部分を青字にしました。
大丈夫だ、問題ない。

次は簡単なシステムコールを追加してみる
まずはエントリの追加から。
# chdir /usr/sys/ken
# ed sysent.c
2131
62
        0, &nosys,                      /* 49 = x */
s/nosys/hello/
s/x/hello/
p
        0, &hello,                      /* 49 = hello */
w
2135
q
この構造体に引数の数とハンドラを登録する事でシステムコールを追加できるはず。そして、実際のシステムコールの実装。ここでは簡単のためconsoleに直接helloと表示することにします。ここも同じくken以下で作業します。
# ed hello.c
?
a
hello()
{
        printf("hello\n");
}
w
32
q
最後にカーネル再コンパイル。
ところがそこには罠が。/usr/sys以下で「# sh run」とすれば、苦労せずにカーネル再コンパイルができる・・・と随所に書かれているのですが、実はこのままではlib1、lib2が更新されず、すなわちken以下での先程の修正が反映されません。そこで、以下の手順でrunスクリプトを修正します。
# chdir /usr/sys
# ed run
3
ar r ../lib1
s/1/1 *.o/
p
ar r ../lib1 *.o
8
ar r ../lib2
s/2/2 *.o/
p
ar r ../lib2 *.o
w
908
q
修正適用後は「# sh run」でカーネル再コンパイル可能。lib1とlib2を完全に削除しちゃうと解決できないシンボルが出てきてしまうので注意。ここは細かく追ってません。また、ken以下の拡張子cを全てコンパイルするように記述されているため、追加したファイルについて気にする必要はありません。

追加したシステムコールの確認
先ほど追加したhelloで呼んでるprintfはC言語の標準関数ではなく、ken/prf.cの中で定義されているconsoleへ直接出力するpanic用の関数。素の状態だと出力禁止になってるので、デバッグのために出力を開放する必要があります。simhを利用している場合にはCtrl-Eでモニタ?に落ちて
Simulation stopped, PC: 002502 (MOV (SP)+,177776)
sim> d sr 1
sim> cont
とすればOK(前回のLions読書会でoracchaさんに教えてもらいました、さんくす!)。
続けて、追加したシステムコールを呼び出すだけの簡単なユーザランドプログラムを書いてみます。適当な作業領域で作業しましょう。
% ed sys_hello.s
?
a
main:
        sys 61
        sys exit
w
24
q
49番で追加したシステムコールですが、アセンブラは8進数で記述する必要があるため61と書く必要があります。ちなみに、hello.sではsys writeなどと書きましたが、実はこのシステムコール名、マクロなどではなく、アセンブラの中に直接「名称→コール番号」の変換テーブルを持ってるようです。なので、アセンブラを修正しない限りは数値直書きしかありません。
という事で、実行して動作を確認。
% as sys_hello.s
% a.out
hello
%
・・・とは、ならず・・・
% as sys_hello.s
hellout


%
こんな風になりました。最後の改行についてCRだけ表示されたタイミングで、カーネル内のprintfがLFを追い越して表示しているようです。ここも細かくは追っていません。まぁ、とりあえずシステムコールの追加はうまくいっているようなので一安心。


システムコールの引数と返り値
システムコールの引数は、レジスタr0とtrap命令に続くテキスト領域内の最大5つまでのデータ列として渡せます。また、返り値はレジスタr0です。
システムコールのハンドラ側では、それぞれu.u_ar0[R0]、u.u_arg[n]として値を受け取ることができます。返り値はu.u_ar0[R0]にセットすれば、システムコールから返るときにユーザランドのコンテキストに書き戻されます。
これらの挙動を確かめるため、やはり簡単なテストコードで実験しましたが、ここでは割愛。

selectの実装
まずはシステムコールとCの関数をブリッジする部分を作ります。適当な作業領域に移動して以下のファイルを作ります。
% ed syssel.s
?
a
.globl  _select


_select:
        mov 2(sp), _nfds
        mov 4(sp), _rfds
        mov 6(sp), _wfds
        mov 10(sp), _efds
        mov 12(sp), _timo
        sys 62
_nfds:  0
_rfds:  0
_wfds:  0
_efds:  0
_timo:  0
        rts pc
w
178
q
システムコールの番号はさっきより1つ大きな値。引数をsys命令の直後にコピーしてシステムコール呼び出し&リターンするだけの簡単なラッパーで、今回R0は返り値だけに利用しました。spからのオフセットも8進数で書くことに注意。これでC言語から「int select(int nfds, int *rfds, int *wfds, int *efds, int *timo);」の形でシステムコールを呼び出せます。本来ならrfds, wfds, efdsはfd_set *ですが、typedefがないのとプロセスが持てる最大ファイルディスクリプタ数が15なのでint *としました。気持ち的には「typedef struct fd_set { int fds_bits[(NOFILE + 7) >> 3] } fd_set」です。timeoutは難しいので今回は無視。というか、nfdsとrfds以外は今回は無視します(笑)。
さて、いよいよシステムコール側の実装です。本来は指定されたデスクリプタのいずれかが変化するか、signalが割り込むまでブロックするのが正しい動作ですが、簡単のためノンブロックで返るような動作にしましょう。
# chdir /usr/sys/ken
# ed sysent.c
2135
62,63p
        0, &hello,                      /* 49 = hello */
        0, &nosys,                      /* 50 = x */
s/nosys/select/
s/x/select/
s/0,/5,/
62,63p
        0, &hello,                      /* 49 = hello */
        5, &select,                     /* 50 = select */
w
2141
q
# ed select.c

?
a
\#
\#include "../param.h"
\#include "../user.h"
\#include "../file.h"
\#include "../inode.h"
\#include "../reg.h"


select()
{
        int nfds, rfds;
        int out_rfds;
        int rc;
        register *fp, *ip, i;


        nfds = u.u_arg[0];
        rfds = fuword(u.u_arg[1]);
        out_rfds = 0;

        rc = 0;

        for (i = 0; i < nfds; ++i) {
                if (!(rfds&(1<<i)))
                        continue;
                fp = getf(i);
                if (fp == NULL)
                        continue;
                if (!(fp->f_flag&FPIPE))
                        continue;
                ip = fp->f_inode;
                plock(ip);
                if ((fp->f_flag&FREAD) && (fp->f_offset[1] != ip->i_size1)) {
                        out_rfds =| (1<<i);
                        ++rc;
                }
                prele(ip);
        }
        suword(u.u_arg[1], out_rfds);
        u.u_ar0[R0] = rc;
        u.u_error = 0;
}
.
w
627
q
#
sysent.cにselectのエントリを追加します。引数の数が5となるため、構造体の先頭の0を書き換える必要がある点に注意。selectの実装は見ての通りです。いくつか注意点を書くと、まずは引数の処理。u.u_arg[1]にはint *rfdsが入っていますが、この値はユーザ空間でのrfdsへのポインタ値です。なので、中の値を見るためにはfuword()を使ってユーザ空間から読みだしてやる必要があります。値の書き戻しも同様でsuword()を使います。それぞれfetch user-space word、store user-space wordですね。もう1点忘れがちなのがu.u_errorの処置。ループ内でgetf()を呼び出しています。こいつはファイルディスクリプタ値を渡して、指定したデスクリプタが開いていたら構造体を返してくれる関数ですが、開いていなかった場合にはNULLが返るばかりでなく、u.u_errorにEBADFがセットされます。このエラーをこのまま放置すると、trapから返るときにu.u_ar0[R0]にu.u_errorを上書きします。必ず0にリセットしてあげましょう。カーネルの再構築も「# sh run」で忘れずに行います。

動作確認
selectの確認はちょっと面倒ですね。pipeで子プロセスを2つ作り、それらの入力を多重化して待ちながら逐次表示してあげるようなプログラムを書いてみます。まずはselectを使う上で必要になるいくつかの関数を用意します。syssel.sを作った作業領域で続けましょう。
% ed select.c
?
a
FD_SET(fd, fds)
int fd;
int *fds;
{
        *fds =| (1<<fd);
}


FD_ISSET(fd, fds)
int fd;
int *fds;
{
        return (*fds & (1<<fd));
}


FD_ZERO(fds)
int *fds;
{
        *fds = 0;
}
.
w
162
q
%
本来ならマクロで書きたいところですが、たぶん当時のプリプロセッサでは引数の置き換えとかできなそうなので、トラブルを避けて関数で書きました。grepするとわかりますが、当時のdefineは定数定義しかしてません。
続けてテストプログラム。
ed main.c
?
a
main(argc, argv)
int argc;
char **argv;
{
        int fds0[2];
        int fds1[2];
        char buffer[32];
        int rfds;
        int rc;


        rc = pipe(fds0);
        rc = pipe(fds1);
        rc = fork();
        if (0 == rc) {
                for (;;) {
                        write(fds0[1], "this is child 0\n", 16);
                        sleep(2);
                }
        }
        rc = fork();
        if (0 == rc) {
                for (;;) {
                        sleep(1);
                        write(fds1[1], "this is child 1\n", 16);
                        sleep(1);
                }
        }
        for (;;) {
                FD_ZERO(&rfds);
                FD_SET(fds0[0], &rfds);
                FD_SET(fds1[0], &rfds);
                rc = select(16, &rfds, 0, 0, 0);
                if (rc <= 0) continue;
                if (FD_ISSET(fds0[0], &rfds)) {
                        rc = read(fds0[0], buffer, 32);
                        write(1, buffer, rc);
                }
                if (FD_ISSET(fds1[0], &rfds)) {
                        rc = read(fds1[0], buffer, 32);
                        write(1, buffer, rc);
                }
        }
        return 0;
}
w
729
q
%
コンパイルして実行してみます。と、その前に。カーネルを作り直したので新しいカーネルでの再起動をお忘れなく。「# sync」してCtrl-Eから「sim> exit」でエミュレーション終了。再度エミュレータを起動してrkunixで立ち上げます。
% as syssel.s
% mv a.out syssel.o
% cc main.c select.c syssel.o
main.c:
select.c:
% a.out
this is child 0
this is child 1
this is child 0
this is child1
.
.
成功です!永久ループなので、適当なところでDELキー(Ctrl-C相当)を押します。という事で、最低限のミッションは達成しました。
応用編としてはselectシステムコール内で読めるデスクリプタがなかったらsleepするように変更すると良いかと思います。適当な新規要因を決めてsleepしてあげて、sleepから返ってきたら再度デスクリプタの走査へgoto。wakeup側では毎回、問答無用でselectを起こしてあげるようにしとけば、最低限それっぽく動くはず。真面目にやるならproc構造体を拡張してp_wchan相当のエントリ、p_wfdset[NOFILE]的な物を追加してあげて、p_wchanがselectならp_wfdsetも調べる〜とかするだけでOKなはず。

・・・なんか簡単だったので実装してみました。今までの形式だと分かりにくいので以下は差分を赤文字にしてソースを転載します。

[select.c]
#include "../param.h"
#include "../user.h"
#include "../file.h"
#include "../inode.h"
#include "../reg.h"
#include "../proc.h"


select()
{
        int nfds, rfds;
        int out_rfds;
        int rc;
        int fdbits;
        int i;
        register *fp, *ip, *rp;


        nfds = u.u_arg[0];
        rfds = fuword(u.u_arg[1]);
        out_rfds = 0;

        rc = 0;
        rp = u.u_procp;

retry:
        fdbits = 0;
        for (i = 0; i < nfds; ++i) {
                if (!(rfds&(1<<i)))
                        continue;
                fp = getf(i);
                if (fp == NULL)
                        continue;
                if (!(fp->f_flag&FPIPE))
                        continue;
                ip = fp->f_inode;
                rp->p_wfdset[fdbits] = ip+2;
                ++fdbits;
                plock(ip);
                if ((fp->f_flag&FREAD) && (fp->f_offset[1] != ip->i_size1)) {
                        out_rfds =| (1<<i);
                        ++rc;
                } else {
                        ip->i_mode =| IREAD;
                }
                prele(ip);
        }
        if (rc == 0) {
                sleep(fdbits, PPIPE);
                goto retry;
        }
        suword(u.u_arg[1], out_rfds);
        u.u_ar0[R0] = rc;
        u.u_error = 0;
}
[proc.h](差分周辺のみ)
        int     p_wchan;        /* event process is awaiting */
        int     p_wfdset[NOFILE];
        int     *p_textp;       /* pointer to text structure */
[slp.c](差分周辺のみ)
wakeup(chan)
{
        register struct proc *p;

        register c, i;

        int fd;


        c = chan;
        p = &proc[0];
        i = NPROC;
        do {
                if(p->p_wchan == c) {
                        setrun(p);
                } else if (p->p_wchan <= NOFILE) {
                        for (fd = 0; fd < p->p_wchan; ++fd) {
                                if (p->p_wfdset[fd] == c) {
                                        setrun(p);
                                        break;
                                }
                        }
                }
                p++;
        } while(--i);
}
という事で、長文に最後までお付き合いいただき、ありがとうございました。

2010年12月12日日曜日

UNIX原典(3) - ラスト

続けてUNIX移植話に関連したトピックを3件ほど取り上げてみます。他にも性能やスケジューラの話なんかも面白かったのですが、UNIX原典の読書記録としては今回の記事を最後にしようと思います。

Fair Share Schedulerについてはこの資料が一番詳しいかな、って感じだったのですが、他のスケジューラとか勉強した時に、また比較で考えてみようと思います。

マルチプロセッサUNIXオペレーティング・システム
「UNIXカーネルの設計」の著者、M.J.Bachによるマルチプロセッサ環境への移植話。ここではMP(multiprocessor)、AP(Associated Processor)を取り上げていて、前者がいわゆる今時のSMP、CMPの話。後者はI/OはマスターCPUに括りつけて、他のCPUはユーザランドの実行だけを行うような単純な方式によるマルチプロセッサ化の話になります。この方式ではカーネル自体、特に排他制御などは不要で、スレーブCPU上でユーザがシステムコールを発行した際、マスターCPUにコンテキストを移し、システムコールは常にマスターCPU上で実行するようにするだけで対応できます。よって、この論文では主にMP上に移植する際の問題について書かれています。

Lions本などでも書かれているように、UNIXではatomicな処理が必要となるリスト探索などは、一時的に割り込みレベルを上げて、リスト探索中に割り込みによる中断と他の文脈からのコードの実行を禁止し、探索が終了したら割り込みレベルを通常に戻す、といった処理(spl6やspl0)をしています。ところがマルチプロセッサでは割り込みレベルを上げたところで、他方のCPUが競合するコードを実行する事は可能なわけで、意味をなしていません。よってMP版UNIXでは、これらの問題に対処するためにセマフォ(*1)が導入されました。

また、sleepとwakeupについてもセマフォの取得と開放(psemaとvsema)に置き換えられました。

セマフォを使う際に問題となるデッドロックについても説明されています。これは一般的なデッドロック回避方法「依存ループを作らない」の言い換えでもありますが、資源に順序付けを行い、ロックは順序付けにしたがって行う、という単純なルールに従うだけでデッドロックは回避できます。

排他制御を行う際には、細かな粒度でロックをかけることが性能の上で重要になってきます。連結リストをハッシュに置き換えてロックの粒度を小さくする方法が紹介されているが、一番競合が起きやすいバッファプールのフローリストについては、うまい分割方法が知られていない、と書かれていました。今時の最新事情でどうなっているのか、知っている人がいたら教えてください(笑)。


IBMシステム/370のUNIXシステムの実現
こちらはIBMのメインフレームへ移植したお話。この移植は370のハードへ移植した、というよりは370上で動くTSS/370をターゲットとして移植したようです。mach上で動くように移植されたLinux、MkLinuxに近いもの、と読みました。理由としては370の仕様がハードごとに細かく異なり、個別の移植が困難だったことが挙げられるようです。360系は長期に渡る使用に耐えた拡張性のあるアーキテクチャ、という事になっていますが、メインフレームの文化を考えると、ファームレベルではワークアラウンドやRASの処理がごった返し、相当なカオスなのでは、と想像されます。

この版のUNIXで特徴的なのはページングの実装方法です。1つの物理ページに対して、リファレンスカウントを用意して、複数の仮想ページに割りつけられるようになっています。当時のforkは、親のメモリを丸々コピーして子プロセスを生成する非常にコストの高い処理でした。そのためBSDではfork後すぐにexecを呼ぶプロセスのために、メモリをコピーしないvforkを用意しましたが、これは、親子でメモリを共有する危険な実装で、vfork後にexecせずにメモリ操作をした場合の動作は保証されていませんでした。この版のUNIXの実装では、fork時にはページのコピーをするのではなく、ページテーブルのコピーと物理ページ側の参照カウントをインクリメントするだけで済んでいます。また子プロセスで書き込みが発生した場合には親と共有している物理メモリの参照をデクリメントし、新しいページにメモリをコピーし、子供のページテーブルに登録するという、いわゆるcopy-on-writeを実装していたようです(*2)。

ディスクブロックサイズも、System III以前が512B、System Vや4.1BSDが1024Bだったのに対して、370版では4096Bと大きかったようです。


UNIX移植の経験
Intel 8086、AT&T 3B20S、3B5、UNIVAC 1100シリーズへの移植について書かれています。今までの2章に比べると目新しい話題はないのですが、AT&T UNIXが8086へ移植されていたとは、ちょっと驚きです。

ただ、この8086 UNIX、単純なソフトの移植というわけではなく、それ専用にPDP-11/70風のオフチップMMUを開発し、それを載せたシステムへの移植だったようです。

またエンディアンの違いの話も出ていたりしますが、今の人から見ればPDP-11のエンディアンが変態(*3)すぎるだけなわけですが。


(*1): 本の中ではDijkstraのセマフォ、と書かれている。いまやあまりにも当たり前の道具として使われているため、発明者について言及されることもなくなってしまいました。
(*2): 勉強不足のため、いわゆるcopy-on-writeの原点がこの実装なのかどうか、知りません。ご存じの方はぜひコメントを。
(*3): PDP-11のエンディアンはリトルエンディアンでもビッグエンディアンでもなく、ミドルエンディアン。リトルもビッグもそれぞれの言い分は理解できるのですが、ミドルだけは意味不明。アドホックにしか思えないのですが。。。これも意義をしっている方がいましたら、ぜひコメントを。

UNIX原典(2)

前回の投稿からだいぶ空いてしまいましたが、読んでみて特に面白かったBlitの章についてコメントしてみます。

Blit - 多重化されたグラフィクス端末
Rob Pikeによる、X以前の世界でもグラフィクス端末のお話。UNIX 7th Edition〜8th Editionの頃の話のようです。何と言っても面白いのが、グラフィクス端末側にもプログラムをロードして実行できる、という考え方。ご存知のようにXなんかの世界では、ユーザプログラムはXクライアントとして動き、Xサーバへは描画命令の単位で依頼を出します。これって、今時のアプリケーションによっては、通信の多い切り口でレイヤーを区切っちゃってるんですよね。VNCやRDPなんかのリモートデスクトップともなるとなおさら。更新のあった描画区画単位でBitmap情報をやり取りするので、通信はすごく多い。

一方で、ウェブの世界を考えてみると、実はもう少し賢い。MVCモデルで言うと、Modelをサーバサイドでセッション管理をしながら実行。Viewについては素材をサーバサイドで提供しつつ、クライアントサイドのブラウザが最終的なViewを構築。Controlについてもクライアントサイドのブラウザが提供するわけですが、Web2.0以降になってくると、この辺はサーバから送られてブラウザ上で実行されるJavaScriptが担っており、気の利いたUIや滑らかな画面更新が実現されています。

Blitの作りはこのWeb2.0に似てたりします。Blitが初めに目指すところは端末の多重化。今で言えば、画面に複数のターミナルを開きたい、というただそれだけの欲求です。サーバ側ではmpxというプログラムが走り、その中で複数のシェルが実行されます。今で言えばGNU screenみたいなものでしょうか。ディスプレイサイドではmtxtermというプログラムが走り、サーバとの間はRS-232(19,200bps!!!)で接続されます。mtxtermは多重化されたシェルの入出力を復元し、複数のウィンドウ上で動くシェル環境を再生。原始的なXに近い外観を提供しています。ただXと本質的に違うのは、ディスプレイ側でも直接アプリケーションプログラムが実行できる点。例えばエディタなどでは、画面を表示して、ユーザのカット&ペーストなどを制御するアプリケーションコードがBlit側にロードされ、このサブプログラムにより編集されたデータが定期的にUNIXサーバ側に渡される、といった具合です。

描画に関連するオブジェクトはシリアライズしてRDPクライアント側に送りつけ、ユーザ側に近い場所で実行。セキュリティ的に難しいところはあるかもしれませんが、今風に味付けすれば、色々と研究しがいがありそうなテーマにも思えます。WindowsなんかではDirectXのコードがクライアント側のGPUを使って動く、なんて話もあるみたい(*1)なので、これから面白くなってくるのかなぁ、なんて思ってます。クラウドを考える際の切り口の1つとしても面白いのではないでしょうか。

百聞は一見にしかず、という事でYouTubeで見つけた紹介動画を埋め込んでおきます。


(*1): Windows 7のRDP 7ではDirect 2D/3Dがクライアント側でレンダリングされる話があったのですが、製品レベルに到達しなかったのか、RCからリリース版に移る際に外れてしまったようです。

2010年12月8日水曜日

YUREX driver for HAIKU

Google Codeでホスティングする練習を兼ねて、以前作ったHAIKU用YUREXドライバを公開してみました。

ドライバの作成にあたっては、Yojiro UOさんによるOpenBSDのドライバuyurex.c(現在はutwitch.cに名称変更らしい)と、カーネル/VM探検隊での発表を参考にさせて頂きました。感謝!

問題は、Haiku OS使っててYUREXを持っているという条件を満たすのが、おそらく世界で私だけ・・・という事なのですが。まぁ、今のところ数少ないHAIKU用USBドライバのサンプルって事で。

ドライバで拾った情報をもとにPULSEで表示するサンプルもあるんですが、こっちはPULSEにアドホックな改変を加えたものなので、あまり公開は考えていません。希望があればライセンスを確認して公開する方法を考えますが、まぁ自分が発表でネタに使う以外は、ほとんど需要はないかな、と。


さて、UNIX原典の読書記録と、Lions読書会#2の感想もあるので、しばらく頻繁に更新したいと思います。カーネル/VM Advent Calendarも面白そうだけど、あそこに名前を連ねるだけのネタは・・・難しいなぁ。

2010年11月4日木曜日

Lions' Commentary (9) - 読書会#1 -

なんと、Lions' Commentary on UNIX 読書会が開かれる、という事で参加してきました。
しばらく独りで読み進めていましたが、まさか読書会に巡り会えるとは!
って、改めて読み返してみると、日記のエントリに1年近いブランクがありますね。
やばいやばい・・・時間が経つのが早過ぎる。

読書会ってのははじめてだったんですが、この会では1章ずつ読み進めます。
まずは時間が与えられて個々に内容を理解。その後、みんなで内容について議論する、と。
初回だったので進め方を決めて、さわりを・・・って感じでしたが、次回からは
月1回のペースで2章ずつ進める事になりました。途中参加もOKだと思うので
興味のある方がいたらぜひ。

1章〜4章は飛ばして実際の内容に入る5章からやりました。
pre K&RなC言語がわけわからんので、やっぱり3章も見とけば良かった・・・
みたいな話もありますが、まぁ必要になったら戻るという感じで。

5章はメモリ管理とpanic用の簡易版printfのところです。
mallocとかprintfとかいう名前だけど、libcの同名関数とは別物なので注意。
内容自体は簡単ですが、癖のある当時の文法が一番の悩みどころでした。

・型について
intとポインタはともに16bit。unsignedという考え方は存在しない。
  unsignedが必要な場合はポインタで代用する!!!
  このためstructのm_sizeの型がchar *になっている。
  ちなみにv7ではm_sizeはsigned shortだったりするけど。。。

・キャストが存在しない
  キャストがないため「*((int *)アドレス直値) = 書き込み値」みたいに書けない。
  そのかわり「->」を使うと型に関係なく構造体だと思ってメンバを探しにいく仕様で、
  構造体の名前空間も単一。これを利用して例えばintのメンバintegを持つ構造体を定義し、
  「アドレス直値->integ」と書く事で「*((int *)アドレス直値)」と等価になる。
  現代人からすると超キモイ仕様。

・大域変数にextern宣言をしない
  これは今でも通用するみたいなので、きちんとC言語の仕様をあたればそういう物かも。
  大域変数はあるソース(.c)に実体を置き、他のソースからはヘッダ(.h)にextern宣言
  された物をincludeして使う、というのが今時の描き方。
[foo.c]
 int global_variable;
 ...

[foo,h]
 extern int global_variable;
 ...

[bar.c]
 #include "foo.h"

 void bar (void) {
  global_variable = 1;
  ...
 }
  なのだが、実はexternは省略しても良い。リンク時に型や初期値の不整合がなければ
  1つにまとめてくれる。実際、coremapとswapmapはsystem.hで宣言されており、
  extern修飾もなければソース側での実体定義も存在しない。

・ldiv/lremはなぜアセンブラ?
  当時のC言語は乗除算は使えなかった? とか効率の良いコードが吐けなかった?
  とか憶測が飛び交ったけど、結局は妥当な理由が見当たらない。
  6th Cでのサポートはoracchaさんが試して通る事を確認しているし、吐かれるコードも
  悪くはない(関数にしなければinlineで展開されるのでprolog/epilogも不要なはず)。
  単純に前の版までアセンブラで書いてあったのでそのまま流用?とも思える。
  あるいはKernel書いてる頃にはコンパイラがサポートしておらず、その後ユーザランドを
  作ってるうちに面倒になってコンパイラサポートが入った、とか。
  v7ではアセンブラは廃止されてCの演算子で書かれてたりするので、この線が濃厚か。
  ちなみに、printnの最後はv6では「putchar(lrem(n, b) + '0');」という全うな書き方だが、
  v7では「putchar("0123456789ABCDEF"[(int)(n%b)]);」という微妙な書き方に。。。
  どう考えても遅いしメモリも食う書き方なので、こう書けるようになったのが嬉しくて
  つい書いてしまったコードに違いない。

・改行後のDEL
  改行がCRとLFにわかれているのと同様、歴史的な配慮?
  consoleがプリンタだった場合、描画位置を左に戻して、紙を送って・・・
  といった時間がかかるので、少し時間稼ぎ。
  プリンタがreadyを返さなければXSTのbusy loopで待つ気もするけど。

・8進数
  DECはPDP1の頃に8進数を使う文化が。。。
  詳細はこの辺とか。

・可変長引数
  stdargみたいな書き方は当然確立されていない。
  もちろん、やってる事はva_startとかva_argの中身と一緒なんだけど、
  実装を隠蔽せずに生でスタックを指すアドレスをずらして使ってる。
  なので、無理矢理ダミーの引数を並べて書いてあるけど、実は意味がない。
  引数を無駄に並べるのはv7で廃止されてるけど、実装はそのまま。

いくつか参考サイトを紹介します。
  1. 2238クラブ または 私は如何にして心配するのを止めてUNIXカーネルを愛するようになったか ... 貴重な日本語情報サイト
  2. Commentary on the Sixth Edition UNIX Operating System ... PDF版Commentaryあり
  3. Dennis Ritchie Home Page ... 6th Edition UNIXのC言語リファレンスマニュアルあり
  4. Operating System Engineering / xv6 ... x86/ANSI版
  5. xv6詳説 ... xv6のソースをひらメソッドで読む
  6. Plan9日記 2010-11-01 ... 読書会参加者のレポート
  7. やる気のないはてだ 2010-10-31 ... 同じく参加者のレポート
  8. PDP1 emulator on flash ... おまけ

近況2010!

だいぶ前に仕事が一段落したので同人を・・・みたいな事を書いたのですが。
やはりどこかにボトルネックがある限り、手が空くって事は許されないのですね。
あまりにも非生産的な毎日になってしまったので、意を決して今月転職しました。
そんなわけで、生活の根っこはだいぶ改善されたような気がします。

転職に至るまで、外の世界にも目を向けようと、前回のblogに書いたイベント参加。
あるいは最近はやりの勉強会なんてのにも顔を出すようになりました。
最近ではカーネル/VM探検隊Lions' Commentary on UNIX読書会なぞに参加して、
結構良い刺激になるとともに、ストレス解消にもなってます。

カーネル/VM探検隊向けにはHaiku OS向けのyurexドライバを書いたりしたので
次回参加時には軽く紹介したいなぁ、などと思ってます。
BeOSの系譜らしく、Pulseで貧乏揺すりの度合いを見える化できます!


Lions'本については昨年から独りで細々と読み進めており、最近になって
元会社の同僚を説き伏せて、内輪で盛り上がりつつあったところでした。
まさか大人数で集まってわいわいできるとは思っていなかったので嬉しい!

あと、ここ数年自宅サーバのマシンやHDDが壊れる壊れる。。。
という事で、だいぶカオスな状態で放置されていたサーバ群。
仮想化や冗長化をすすめ、だいぶ快適さが向上しました。

加えて買ったきりでセットアップもままならなかったmotif-rack xs。
こいつもLogicとCubaseからストレスなく使える環境が整った!
だいぶ遅れて迷惑をかけてしまってるんですが、Doll's Ingramの曲は
この環境を使って作曲作業を再開しています。

rackはYAMAHAが音色定義を配ってないので、motifの設定を流用。
あと、Logic用は面倒な手順が色々と書かれてるんだけど、裏技的な方法として、
エンバイロメントのウィンドウを開き、レイヤーとしてテンプレートファイルを開く、
とかやると便利です。
テンプレートから設定をコピペしたり、作業をテンプレートから開始したり、
とかいう煩わしさ、制約から解放されます。

2010年10月2日土曜日

デーモン君のソース探検

中古で買った読み物シリーズ。

ずっとカーネルの手引書だと勘違いしてたんだけど、実はユーザーランドのハックの話。難易度的には低いので「Cの基本は理解していて、読み書きは普通にできます」くらいの人でも大丈夫。UNIXの伝統的なユーザーランドプログラムやライブラリなどを題材に、ソースの探り方、UNIXのお作法、などを指南。

さっと一読したけど、自分にとっては探検12 scriptで出てきたptty周りのお話、探検16 malloc()で出てきたPHK mallocのお話あたりが勉強になった。というか、もう一度読まないと、理解しきれていない。

パパ:え? 最近は中学校でもmmap()を習うのか?


とか、そんな親子の会話。私もしてみたいわー。


2010年9月9日木曜日

UNIX原典(1)

ベル研メンバーによる開発秘話本って事で、読み物として買ってみました。オリジナルはBell System Technical Journal 1984年8月号第2部UNIX特集号らしい。

技術書というよりは歴史書として買ったつもりだったんだけど、結構面白い。ライオン本だと「どう実装しているのか」は理解できても、「どうしてそう実装したのか」までは理解しきれない点も多々ありますが、この本読んでると、バグの話なんかも出てきて、意外なところで実装の意図が読み取れるかも?まだ読み始めたところなんで最初だけかもしれませんが。とりあえず面白いなぁ、という話があったので、それについて書いておきます。

file descriptorの実体については"Lions' Commentary (7) - closeとunlink -"で書いたとおりで、プロセスに直接ぶら下がっているのは実体ではなく参照です。プロセス内でユニークな番号が振られているので、プロセス固有のリソースのような気がするわけですが、実はシステム固有のリソースとして実装されている。forkの話もあるので、なんとなく「そうすべき」というのは直感としてあるのですが、いまいち必然性までは理解できていませんでした。

初期のUNIX@PDP-7ではShellがCP/MのCCPに近い実装になっており、ユーザ・プログラムではなくOSの一部として動いていました。その後、forkとexecが実装され、shellがユーザ・プログラムとして分離。今風のプロセス制御も導入されて、バックグラウンド実行なんかもできるようになりました。この頃はfile descriptorの実体がプロセスにあり、以下のようなコマンド(今風に書きなおしています)が正しく動作しなかったそうです。
$ (echo foo; echo bar) > log

期待される結果は
foo
bar

ですが、実際には
bar

になってしまう。ははぁ、確かにforkとexecの挙動を考えると、file descriptorがプロセスにぶら下がっていると、そういう事になりますね。このバグに気づき、file descriptorの実体はプロセスの外に追い出された、との事です。なるほど。
また、当初はchdirも外部コマンドとして実装されていたそうです。プロセス制御がなかった頃はカレントディレクトリはttyに括りつけで1つで良かったわけで、chdirはこの括りつけのカレントディレクトリを変更して親のShellに戻っていました。ところがプロセス制御の導入によりカレントディレクトリはプロセスごとに個別に持たせる必要が生じ、その結果として外部プロセスとしてのchdirを実装できなくなりました。最近のUNIXから入ってると、むしろ初歩的なトラブルって感じがしちゃいますが、当時はUNIXの生みの親たちですら理解に苦しんだバグのようです。コロンブスの卵的なところがあるのかな。

2010年8月28日土曜日

The Singularity System

ここのところ、まったく時間がなかったのですが、出張帰りの飛行機で時間があったので、Communications of the ACMに寄稿されていたSingularityの記事を読んでみました。

SingularityとはMicrosoft Researchによる研究段階のOperating Systemです。micro kernelを採用しており、コストの高いプロセス間通信を解決する方法としてSIPと呼ばれる機構と、高位言語による記述を採用している点が特徴かと思います。

大雑把な説明はWikipediaの解説が手っ取り早いでしょうか。

Singularityでは、OSを含めて高位言語で記述するのが基本です。kernel本体はSing#と呼ばれるC#の拡張言語で記述され、他にはC#、F#、Visual Basicあたりがサポートされています。

Wikipediaを見るとSIPはmicro kernelと各サーバ群との通信を最適化するための仕組みのようにも見えますが、記事によるともっと一般的なプロセス分離の仕組みのようです。

Singularityにおけるプロセス保護は多層で実現(multiple layers of protection)され、基本的な方式がSIP、二次的な方式が仮想メモリ空間による分離(近代的なOSが一般的に採用している方法)になります。

SIPによる分離では、同一メモリ空間内にページ単位で複数プロセスを貼り付け、仮想メモリ空間切り替えによるプロセス切り替えコストを除去する事が目的となっています。ページ保護は利用しているようなので、SIPで分離されたプロセス間の切り替えでは、ページのアクセス権限だけ書き換えるのかな?この辺は詳しく書かれていないので想像です。このへんの保護は言語レベルでのサポートや形式検証なども使えるみたいです。

一般的にプロセス単位で仮想メモリ空間を切り替えるメリットは、メモリ保護だけでなくメモリ管理にもあります。プロセスにつきヒープとスタックという二種類の可変長作業域が必要となります。一般的にはヒープは下位アドレスからプラス方向に、スタックは上位アドレスからマイナス方向に伸びていくことで実現していますが、同じアドレス空間に複数のプロセスを配置するとなると、ヒープを拡張しようとしたら、別のプロセス空間が邪魔で・・・なんて状況が起き得ます。

680x0時代のMac OSでは、MMUが使えなかったため、ハンドルとコンパクションという考え方で対応してきました。メモリはハンドルで管理し、必要なときだけロックをかけてハンドルからポインタに変換します。使い終わったメモリはすぐにアンロックすることで、ポインタは無効になります。このお約束を守ることで、連続したメモリ領域がなくなった時、コンパクションによってロックされていないメモリを移動し、メモリ再配置による最適化を行うことができるようになります。

Singularityでこの辺りをどう対処しているのかは読み取れませんでした。Garbage Collectionによるメモリ管理をOS含めて導入しており、runtimeによるポインタチェックも行っているようなので、その気になればコンパクションもできるのかもしれません。今時のメモリサイズであれば、コンパクションがなくて実用上問題にならないのかなぁ。

あるいは、記事の例を見ると、そうは言ってもSIPによる分離は共有ライブラリやコンポーネント、Plug-insなどのレベルでメモリ保護を実現するための機構として利用しているようにも見え、実際には仮想メモリ空間による分離もかなり積極的に使っているのかもしれません。

SIPを導入するためには、Javaのような実行時のポインタチェック、配列領域チェックなどが必要になります。この記事の結論では、これらのruntimeのコストは5%程度の効率低下に留まり、仮想メモリ分離をやめたことによるパフォーマンスゲインが38%もあるため、総じて安い、という事のようです。

SIPを使うとプログラムはリロケータブルにする必要があるので、元々共有ライブラリで実装されていたような物は差がないんでしょうが、プロセス分離に適用しちゃうとローダがリロケートするコストが馬鹿にならないような気はしました。

SIP間通信の仕組みとしては、強く型付けされたパイプを使っているようです。実データは同一メモリ空間内のExchange Heapと呼ばれるところに格納され、ゼロコピーでデータ交換しているように読めます。このあたりが効いて、low latency、high bandwidthなプロセス間通信が実現できる、という事のようです。また、通信には"contract"と呼ばれる仕組みが使われており、この辺も突っ込んで調べてみると面白そう。通信データを入出力としてステートマシンを記述して制御しているようなので、形式検証を使ってプロトコルチェックとかできるのかも。

最後の方では、kernelにGarbage Collectionを適用する事の是非について触れられています。kernel objectの多くはlifetimeが非常に長く、プロセスの開始から終了まで張り付いています。この種のobjectを大量にGCで管理したところで、メモリ回収のwalkingにかかるコストが大きくなるだけで、あまり旨みがないのでは、といった議論があるようです。

という事で、最近は懐古趣味ばかりでしたが、めずらしく先端研究についても調べてみました。

2010年5月9日日曜日

オリゲー・フェスタ68

通称オリフェスに参加してきました。
自分のサークル名義じゃないけど、久々のイベント参加。


イモプロ名義で以下の物を展示してました。

  • MegaZ-80K(最初の写真では画面左端より外側w)

AVRをふんだんに使って再現したMZ-80K互換機。MEGA644でZ80エミュレーション。UARTベースで共有バスを張って、その上でI/O通してました。今回は足がいっぱいあったので、SRAMとはアドレス15bit、データ8bitを外部ラッチ無しで直に繋いでます。配線は圧倒的に楽ができた(笑。








画像表示はMEGA328が専任。ひたすらUARTモジュールを駆使してNTSC信号を作りまくるお仕事。I/Oからのアクセスにすぐに応答する余裕がないため、間にFIFOとしてのTINY2313がいます。
I/OバスとしてはシリアルでTINY2313がデータを掴み、HSYNCを待ってパラレルでMEGA328に流し込む感じです。

写真はMac上のGtkで書いてたプロトタイプに、シリアルで画面表示モジュールにデータを流し込むコードを追加し、エミュレーションで書いた画面と実際のビデオ出力を比較しているところ。











あとはサウンドもTINY2313が担当。こっちは割り込みでレジスタをパタパタさせて鳴らしております。

で、最後はなぜかファミコンコントローラ。色々とツッコミもありましたが(笑。キーボードどうしようかな、PS/2にしようかな、USBにしようかな・・・と考えていたらPS/2の端子が部品箱にない事に気づきました。USBならいっぱいあるけど、ホストは作ったことはないし・・・。という事で、手っ取り早く済ませよう、とファミコンを投入。やっぱりTINY2313を使っていて、キーボードに対するI/Oアクセスが来たらコントローラのステータスを読み出し、適当にスキャンコードにマップして応答を返しています。


  • CP/Mega88(左端、iBook)

再びCP/Mega88。こっちもインベーダー動かしたりして展示してました。
ライセンスはどうなってるの?って人もいましたが、今は開放されてます。


  • USB-PSG/SID(中央、MacBook)

USB接続のPSG音源とCommodore64のSID音源。Linux用のドライバしか書いてないので、デモはUbuntu上で行いました。fMSXのPSGアクセス部に手を入れてドライバのI/Oを叩くようにしています。Ys-IIのサウンドモードを動かしていました。
同じような事をやってます、って話をしてくれた人が何人かいましたが、Windowsでやってる人はドライバ部分で苦労してるみたいです。Linuxはその辺がお手軽だったので、独自クラスで適当なbulk転送のプロトコル決めて、ちゃちゃっと処理しちゃってます。
/sys/bus/usb/drivers/usb_psg/ 以下にデバイスファイルが出来て、そこ経由でレジスタ読み書きしたり、レジスタダンプ表示させたり。
USB部分はPIC18F2550ですね。今回唯一のPIC成分。PICだとUSBフレームワークがついててファーム開発もらくらく・・・というわけではなく、実は自分、商用コンパイラ使うのが許せなかったので、sdcc向けにスクラッチでUSBのファームを書きました。当時は自前でUSB周りを叩いたファームって見かけなかったけど、今だとどうなんだろう。シリアルからデバッグメッセージ吐きながら開発してましたが、うかつにメッセージ吐くとUSBのプロトコル側でタイムアウトが発生してしまうので、結構大変だった記憶があります。

Kerne/VM探検隊

Kernel/VM探検隊#4に参加してきました。
今回も色々と刺激的なお話が聞けました。
技術的にも、年齢層的にも、けっこう幅広いのが良いです。

yojiroさんのUSBデバイス解析の話は面白かった。
「俺の電線に流れる電気信号を観測して何が悪い」って、なるほど。
電子工作のデバッグでプロトコルアナライザ使ったら敗北感がありますが、
ソフト開発では漢のツールですね。

kobaさんのqemu+chrootの話もgood。
クロス環境作成にはいつも苦労するので。
こういう発想はなかったな。。。
linuxの/proc/sys/fs/binfmt_miscも今回はじめて知った。
そういえば、UNIXの「#!」の習慣はどこから始まったんだろう。
Ancient UNIXではこの習慣はなかった。
実行ファイルでなければシェルがそのまま実行してますよね。

ucqさんはバイナリエディタでプログラミング。
Plan 9を例にしてたけど、やっぱり一番簡単なのはX680x0の.r形式だな。
ヘッダ一切なしで、relocatableな実行列の塊。
自分は開発環境が入ってなかった部室のマシンで、しかたなく
TYPEコマンドでリセットコマンドを作った記憶がある。。。

shudoさん参加してたのか。
しかも発表側。
末尾再帰で最適化かかってるんじゃない?的なコメントもあったけど、
その時はスタックは呼び出し元と整合するように補正してからジャンプ
ってなるはずだし、やはり元作者のケアレスミスでは。

oracchaさんの発表でsocketはshutdownかけると相手側のreadが0で返り、
それをEOFとして処理するアプリが多いという事を知った。
signalが来た時もsystem call中断してhandlerに処理を移すけど、
あの時は-1か。確かにman 2 readに書いてある。
BeOSのsshがEOF検出できずに固まる事があったけど、あれはshutdownが
きちんと動いていなかったからなのだな。。。と今更気づいた。


そうそう、今朝、目覚めてふと疑問に思いました。
ファイルシステムのジャーナルってどの層でやってるんだろう。
普通UNIX系のシステムだと、デバイスの上にブロックデバイス
ドライバがあって、その上にバッファキャッシュが載っていて、
さらにその上にファイルシステムが載ってるはず。
トランザクションって意味ではファイルシステムの最下層で
ジャーナル録りたい気がするけど、その下のバッファキャッシュが
LRUで動いているので、それでは不意の終了に耐えられない。
かといってディスク直上でジャーナル録ってたら、
◯△fsのジャーナル機能が云々〜って話にはならないはずだし。

2010年4月27日火曜日

エレキジャック・フォーラム

というわけで、発表させていただきました。

発表資料のほうは、カーネル/VM探検隊で見たsexy hookの
プレゼンに触発されて、Preziで作ってみました。


オンラインでの参照はこちらから。

デモをしようと思ってたんだけど、ディスプレイ出力端子が
USB端子とぶつかってしまい断念。
この日にあわせて新マシンを調達したのが裏目に出たか(うひ

そのうち基板作ったら一式公開〜とか考えてたんですが、
興味もってくれた人がいたので、近いうちに一度公開しようと思います。

2010年3月30日火曜日

思考の整理学 (1)

電車通勤をやめてからすっかり本を読まなくなりました。
積まれていた本の中から1冊だけ鞄に入れてたんですが、今日は少しだけ読む機会があったので、読んだところを少しだけメモ(続き読むが先になるかもだし)。

という事で、表題の通り、外山滋比古さんの思考の整理学。
整理学とかいうタイトルだけど、特に考える事に関して体系化した書物というわけではないですね。「思考」に関する様々な角度からのエッセイを、どちらかと言えばごちゃっとまとめた感じ?

「グライダー」:グライダーと飛行機。詰め込み型教育の産物、自力で前へ進む事のできない人たちをグライダーに例えて批評。「自分で翔べない人間はコンピューターに仕事をうばわれる。」1986にまとめられた本にしては刺激的な台詞で締めくくられているけど、2010年になった今日、状況はどんなもんだろう? 就職難と人材不足の共存状態は、予言が的中した結果とも思えるけど、実際には大企業はまだまだグライダー人間に支配されている気はするなぁ。
続く「不幸な逆説」で述べられている、教える事を渋る事による飛行機人間の育成、という考え方は面白い。伝統芸能などで見られる教えない文化。師の業を盗もうと努力するところから学ぶ姿勢、意欲、独創性などが生まれるのではないか、という仮説。

「朝飯前」:まさに自分が修論の時期に実践していた事。一日のうち頭が働くのは起きた直後の数時間。ここまでは大学の仲間うちでも周知の事実だった。なので、一日を12時間ずつにわければ、頭が働く時間も二倍になるだろう、と。3時間寝て9時間作業〜みたいな生活だったと思う。普通に仕事して、論文も書いて、さらにバンド活動・・・となると、こうでもして時間を稼ぐほかなかった。凄い勢いで時間が過ぎていくようで、カレンダーは半分しか進まないという不思議な感覚で、実際に生産性もかなり上がった気がしました。
自分がやってた事と同じ事を筆者がしていると知って嬉しくなり、それがblogに書き留めようと思ったきっかけでもあるわけですが。さらに筆者は、朝飯を抜けば、さらにこの朝飯前の時間を長くとれる・・・と、なんだか子供じみた事まで(笑。偉い先生がこういう事を実践している、というのは生々しく、結局は努力なんだ、と思うと、なんだか救われた気持ちになります。努力でどうにでもなる世界、つまるところそれが究極の平等社会なんですけどね。民主党政権しかり、どうも日本は変な方向に向かいつつあります。

アマゾンでも誰かがレビューに書いてましたが、アイデアについてのエッセイでのべている事はジェームス W.ヤングの本に書かれている事と同じですね。学生の頃に読んで意識、実践してきたけど、アイデアが生まれるプロセスに関してこの本の解析はかなり正しい。色々な人が共感している事からも万人にとって真なんだと思う。うまくやれば凡人でも非凡のフリができる。。。はず。



#そういえば、内閣府調査の一環で面接がありました。大学院教育はどうあるべきか、というテーマで議論したのですが、つまるところ調査の趣旨は「ゆとり教育は失策だった。その分、大学院教育でフォローして、なんとか社会で使える人間に仕上げる体制を作りたい。」という事だそうです。大学、大学院の進学率が伸びていますが、今まで12年で身になっていた事を16年、18年じっくりかけて教えているだけ。こういう状況にあって、自分の子供にどう学ぶ事を教えていくべきか、非常にむずかしい問題だと感じています。未だ脳の衰えを感じる事はありませんが、知識に関しては若ければ若いほど吸収が早く、使える技術になり易いのは事実でしょう。

2010年2月26日金曜日

ファミ・コンパーチブル(改)

あきばお〜で買い物ついでに。。。
600円弱とちょーお安かったのでw

背面についてる端子は写真の通り。
コンポジット端子なのでその辺のモニタに気楽にさせるわけですが、ちょっと配置が変(笑。付属のケーブルも赤/黄だったりして、なんじゃ? って感じ。

答えから言うと、白と赤にはまったく同じ信号が配線されてます。日本ではステレオに繋ぐ時にもL(白)だけ繋げれば自動で左右に展開されるのが一般的ですが、大陸では違うのでしょうか。。。謎は深まります。

ちなみにACアダプタ端子が付いてますが、添付の紙っぺらによれば使用禁止。
・・・PSE通ってないから? 一応電池4本でも動くんですけど、電池は面倒です。
「まぁ、その辺のアダプタをさせば動くだろう・・・」
と甘い考えでその辺に転がってたアダプタをさしたら

ぷつんっ
「あっ」
・・・ぷ〜ん(異臭)・・・

やべー、やべー。
ってことでさっそく分解して調べてみたら極性が逆でした。

しかし、この電源作りが怖いです。。。
アナログ弱いんで理解が怪しいですが、トランジスタ(8550)1つで定電圧回路を作ってました。エミッタベース間に逆電圧をかけてツェナー効果で6Vを作っている模様。

ってなもんで、試しにセンターマイナスのアダプタを挿してみましたが、電圧が不安定なせいで画面がぶれまくり。

しかも、めちゃくちゃ熱くなるので要注意。。。

これじゃ使えないな、という事で例によってUSBから電源拾えるように(笑

写真の位置が空きスペース的にも固定するにもあつらえ向きでした。ここからUSB配給の電源を電池ケースの+/-に繋ぎ込めばOKです。

電圧が6Vから5Vになりますが、まぁ大丈夫でしょう。というか、むしろ5Vが本命な気はする。

で、多くの互換機と同じように拡張音源に対応してなかったので、ついでにその辺もプチ改造。カートリッジの45番ピンと46番ピンがショートしてるので、パターンカット。カートリッジから出力端子に繋がってるケーブルのA(udio)線をカットして、基板側を45番に、A(udio)線を46番に繋ぎ込めばOKなはず。

という事でためしにマダラを。

う〜ん・・・鳴るようにはなったけど、バランスがいまいちでした。FC音源側が極端に小さく鳴ります。

45番と46番の間にダイオードと抵抗挟むくらいで調整できないかな。。。
アナログ詳しい人いたら弟子入り希望(笑

という事で、ちょっと息抜きの寄り道でした。満足したのでこいつはステステ(ぇ

(注)この記事は無保証ですので、改造はAYORでお願いしますね。

2010年2月24日水曜日

浮気なぼくら

そのうちハードからOSに・・・とか言っておきながら、実は最近の休み時間活動はVNC@Haikuだったりしました(笑。

普段、設計用の端末ではUbuntuが動いてたりするんですが、そろそろ飽きてきた(かつ、ドライブの空きがなくなってきたw)かなぁ・・・と。

端末ならVNCが動けばたいていは事足りるので、ここは一つHaikuでも、と思ってBeOS用のVNCを動かしたんですが、なんでこんなに・・・ってくらいに糞重い! なんでだろ・・・とか思ってVNCのプロトコルを調べてみたら、結構まとまった資料になって公開されている模様。それならば・・・という事で、プロトコルの調査がてらクライアントを作ってみました。

もともとVNCはアプリケーションレベルで色々組み込んでも遊べるかなぁ・・・って事で、興味は持ってたんですよ。例えばAVR上のMZ-80Kエミュレータの画面がVNC over UARTで飛んでくる、とかね(笑。

とりあえずハマったのはtwitterでも呟いてた認証の部分。標準のVNCAuthではDESを使って認証するんだけど、暗号化する時のパスフレーズ、なぜかバイト単位で上位下位を反転してやらないといけない。自分の書いたDESが間違ってるのかと思って、色々なライブラリと期待値比較しちゃったよ。。。

とまぁ、そんな感じでボチボチ動くようになったVNCクライアント。とりあえず家でも32bit rawという一番単純なプロトコルでOS Xの画面共有に接続してみたけど、LANで使ってる分には普通にサクサク動く感じ。なんで世の中のBeOS向けクライアントがこんなに重いのか・・・わけわからんちんですよ。

カーネル/VM探検隊

同じ会社にしばらくいると、少し外の空気を吸わないと・・・という気分になります。
Lionsもその一環なのですが、しばらく前にngcomで「最近は勉強会が流行っている」
という話を聞いたので、なにか面白そうなものがあれば参加したいな・・・と思い
目をつけていたのがカーネル/VM探検隊。ちょうど仕事にも余裕が出てきたところなので、
平日開催という敷居の高さを乗り越えて(午後からエスケープしたとも言う)参加して
きました。

まったく知った顔がいない、という状況は凄く久しぶりで、言葉通じるのにまるで
海外出張みたいにドキドキしてしまいました(笑。そうは言っても、実は大学の同期も
いたのですが、すいません人を覚えるのがトコトン苦手な私。。。うっすらと記憶に
あるものの、よく覚えていませんでした(汗。一応、だんだんと記憶は戻ってきて
最後にはなんとなく思い出してたんですよ。。。とフォローはしておきます(笑。

本業の面では畑違いなんですが、Plan 9とか、SheevaPlugとか、プライベートな嗜好では
思いっきりストライクゾーンで、人見知りとは別の意味でドキドキワクワクな時間でした。

LTのタイマー割り込みの話とか妙に盛り上がって(一部の?)参加者のハイレベルっぷりに
ときめいたりしてました。自分はv6のclock.c callouts近辺のコードしか頭になくて、
どこまで話についていけてたかわかりませんが。。。
先日の社内でのsignal話もそうなんですが、今まで身近にkernel hackerがいるって状況に
なかなか巡り会えなかったので、最近のこの体験はちょっとした感動です。

そのうちLTネタくらいは出したいなぁ。

P.S.
スライドがド派手な人が多くて驚いた。最近はこれが普通なのか。。。あと、OS Xを
使って長いくせに、画面の一部を拡大したり・・・って機能をまったく知らなかったorz

2010年2月18日木曜日

脈々と続くV6の血

仕事で、Linux kernel担当者のところへsignal周りの処理を質問にいった。
コードを見ながら色々と説明してもらった。

ところどころSMPを意識したコードが入ってはいるが、
基本的な処理の流れはV6のそれとまったく同じだという事に驚いた。

計算機に携わる以上、Lionsは一通り読んでおいて損はなかったな、と思った。





と言いつつ、ちょっと昼休みのコード読解は止まってまして、
しばらく思いつきで始めたこんなことをやってました。

CP/Mの次はMZ-80K、700、その次はApple II・・・と少しずつ
複雑なハードを再現したいなぁ・・・とか思ったりしてるのですが、
適度なところで飽きて、またOSに戻ってくると思います。

要素技術は身につけたので、AVR上でV6相当のOSを・・・とか
思ってたりするのですが、壊れたLANTANKを、とか玄柴さんを・・・とか
遊ぶネタは盛りだくさんです。LANTANKを会社に持ち込んで遊ぶのは
ちょっとアレかもしれませんねぇ。。。