|
HMDT - Back Number / Semptember, 2000 |
September, 2000■Mac OS X de Java - Java から Carbon?Mac OS X PB についてくる Java のクラスファイルを眺めていたんだけど、com.apple.mrj.macos っていうパッケージでは Classic Mac OS の関数呼び出しをサポートしているんだね。知らなかった(昔懐かしい JDirect っていう呼び名を使ってもいいのか?)。あわてて、Mac OS 9 に付属している MRJ Classes の方を見てみたら、こっちにもあった。でも、9 の方は途中までしかメソッドが入っていなかったり、空のクラスがあったりで、まだきちんと実装していないみたい。X の方は 9 よりはいいようだ。きちんと検証していないけど。 と、いうことは Java で Carbon App が書けるのか?どれほど意味があるか分からないけど。う〜ん、Mac OS X の Java はいろいろある。 ■Mac OS X de Java - Java から Cocoa を使ってみたMac OS X の特徴として、たくさん API があって、その内の一つ Cocoa は Java からもシームレスに使える、というのがあるよね。 Java と聞くと、「えー、おそいじゃん」という声も聞こえてきそうだ。じゃあ、従来の Java と Cocoa を叩くときに速度に違いはでるかのか?実験してみた。 まず、起動時間。ウィンドウを表示するだけの簡単なプログラムを書いて、時間を計測してみた。 これは普通の Java を使う場合。AWT (Abstract Window Toolkit) を使っている。このプログラムは他の Java 環境でも動くよ。
こっちは Cocoa を使ったみた場合。手探りで書いたので、これで正しいかどうかは良く分からない。あまり参考にしないで下さい。このコードは Mac OS X でしか動かない。
両方ともただ単にウィンドウが表示されて終わり、のプログラムです。さて、起動時間は、、、
む、Cocoa は早いじゃん!やった!と、単純に喜べるわけではなくて、理由はいくつかあります。まず考えられるのが、Java は起動後にクラスを動的に読み込むんですよ。何のことやら分からないって?ここから先は興味ある人だけが読めばいいと思いますが、Java アプリケーションは実行するのにいくつかのクラス(ライブラリみたいなもんだ)が必要です。で、それらは java(小文字で書いてある場合は Java 仮想マシンとしての java)が起動した後、必要なものを必要なときに読み込むんです。じゃ、それらはどこにある?AWT の方は、/System/Library/Frameworks/JavaVM.framework/Versions/1.2/Classes/classes.jar です。で、Cocoa の方は /System/Library/Java の下。AWT の方で使っている .jar ファイルは Java のクラスを 1 つにまとめたもの。だから読み出すときは一気に読んで読みほどかなくちゃいけない。それに対して、Cocoa の方はフォルダの中を見てもらえば分かるけど、クラスファイルがばらばらのまま置いてある。だから読み込み時間は短い。だから起動時間は早い、ってことになるわけだ。 それを差し引いても、Cocoa での起動時間は早い。まったくストレスを感じない、っていうのは嘘だけど、ちょっと待つぐらいで立ち上がる。最近はあまり触ってないけど、一年前ぐらいの Win での Java2 はとにかく起動が遅かった記憶がある。あれと比べると非常に使い物になってると思う。だれか、現在の Win 上での起動感覚が分かるならば、教えて下さい。 本当はここで、Objective-C で書いたときと比べたいところ。多分、もっと早いだろうけど。今回の結論は、Java で Cocoa プログラミングを書いても、「Java だから遅くて使えないだろー」とはいちがいには言えない、ということ。非常に明解さを欠く結論になってしまいましたー。 ■初 system panicさ〜て、今日は何をしてみようかなー。うん、待てよ?たしか、おれは Mac OS X Server の CD を持っていてたよな?と、気付いてしまったところから、悲しい出来事は始まった。 Mac OS X Server には開発環境が付属していたよな。ということは、これをいれれば cc (c compiler) が使えるぞ。おお、もしかして Web Object も使えるのでは?と、推論をすすめて、Mac OS X Public Beta で、Mac OS X Server の Development Tool は使えるかどうか、早速インストール実験をしてみました。 CD を入れて、パッケージをダブルクリックすると、ちゃんと X 用のインストーラが立ち上がる。おぉっ、いけるんじゃない?という期待のもと、ダイアログを進めていくと、なにやらコピーを開始しだしました。やったじゃん。まぁ、もともと同じ OS だしね。と、思いながら待っていると、プログレスバーがなにやら動かなくなりました。あり?やっぱりだめなの?まぁ、しょうがないか。とりあえず、インストーラを終了して、、、あれ、終了できないや。まぁいいや、cmd + ctrl で再起動っと、、、 と思い、再起動をかけました。すると、いつも通りの Happy Mac が出たかと思うと、なんと system panic の文字が! ええええぇーっ!panic したよぅ!Darwin 解体新書やっていたから、panic が出てくるっていうのはコードの上では知っていたけど、まさか本当に目にするなんて〜!実際、Happy Mac の後ろに、いかにもコンソールっていう感じでエラーメッセージが出てくるのは、迫力があります。あぁ〜、画像を保存できなかったのが悔やまれる(悔やんでいる場合か)。メッセージによると、"init died, Wainting remote debugger" だそうで。初期化が失敗したようです。ちなみに、panic とは kernel が死んでしまった状態です。爆弾よりたちが悪いと思っていいと思います。 その後、いろいろ試したけど、結局復旧できなくて再インストール。まぁ、自業自得なんですけどね。Mac OS X Server ユーザの方は、Mac OS X Public Beta に Development tool をインストールしないことをお勧めします。素直に X 用の Develop tool のリリースを待ちましょう。でも、X Server を持っている人の絶対数は少ないからこんなことをする人はいないと思うけどね。 ■Mac OS X de XML今日は XML 環境について実験してみました。 まずは、標準でついてくる Internet Explorer で試してみる。Classic Mac OS 用の IE には、XML のパース機能がついていたはず。では Mac OS X では、、、?結果は NG 。 アラートによると、MS XML Library がロードできないとのこと。正規バージョンではついてくることを期待したいですな。 じゃあこれだ、というわけで取り出してきたのが、IBM の XML Parser for Java。alphaWorks から無償でダウンロードできます。Java で動く Parser としては、圧倒的な支持を集めてるこのツール。Pure Java だから問題なく動くはず、、、 結果は問題なし。LookAndFeel を変えても問題なし。ただ、動作速度はあまり速いとは言えず。 Mac OS X では、標準のフレームワークに XML Parser 機能がついていると思われるんだけど。API Document がないから分からない〜。まあ、標準以外にも選択肢があるということは、歓迎すべきことでしょう。 なぜ、Mac OS X では XML Parser が標準でついてくるのか?それは .app の Package にも関係してくるんですよね。それらに関しても実験してみようっと。 ■2 つの気になるフレームワーク:opcode.framework と PowerPlant.frameworkMac OS X Public Beta の /System/Library/PrivateFrameWorks というフォルダには、いろいろ面白い framework がある。特に目につくのは、opcode.framework と PowerPlant.framework。 ちなみに .framework とは、NeXT から使われている、ライブラリやリソースをまとめたパッケージ(でいいのかな?)のこと。これをインストールしておくと、各アプリからその機能を使用することができる。 opcode.framework は MIDI のサポート用だろう。Apple が純正の MIDI インタフェースを提供するという噂があったけど、これで登場が近いことが確認できる。 もう一つは PowerPlant.framework。PowerPlant は Metroworks が誇る、Mac プログラミング界最強のフレームワーク。現在の Mac ソフトの 8 割はこれを使っているでしょう。その PowerPlant を framework 化して標準でついてくるの?もしそうなら、多くのデベロッパには朗報でしょう。これで多くの Mac ソフトのサイズが 2MB 近く小さくなるかも? ■Mac OS X de Swing!◆Mac OS X で Swing は動くの?Mac OS X Public Beta を手に入れたので、Swing 環境のテストをしてみました。Sun が配付している SwingTest を動かしてみると、、、 結果は OK。コンパイル、起動共に問題なし。ただし、動作は重い。特に、コンポーネントの再描画をさせるような処理だと、非常にもたつく。 これじゃ使えないよなー。Swing はきついのかなー、と思いつつ、インストールしてある UIManager を調べてみました。すると、Metal、Motif、Windows、Mac があることを確認。ふーん、ちゃんと Mac も入っているんだ(他環境用の JDK の Swing には、Mac LookAndFeel は標準では入っていない)、と思いながら良く見ると、パッケージが "com.apple.mrj.swing.MackLookAndFeel" になっているじゃないですか。ということは Sun が配付しているやつと違うのか(その場合い、sun.javax.swing で始まる)?と、思いながら動かしてみると、これが Aquafied Swing でした。 ◆Aquafied Swing!どうやら、Apple は Swing API を実装する際に Cocoa を使った模様。Swing の各コンポーネントが Aqua 化されてました。 動作は良好。他の Swing コンポーネントに比べると、はるかにサクサク動きます。そして美しい!いっちゃあなんだけど、Metal とかってかっこわるい、、、 Aquafied Swing はおそらく Pure Java ではないでしょう。もちろん、これは Swing コンポーネントの portability と light-weight implementation に反しますよね。しかし、動作感と見た目の美しさは捨てがたい。ここは、Swing のリッチな API を使って高速に動作する環境が一つできた、と考えるのがいいのではないでしょうか。 今まで Swing でアプリを書いていた人は、ぜひ Mac OS X 上で動かして見て下さい。充分なパフォーマンスと美しさが得られます。 Apple の Mac OS X Public Beta Release Notes: Java で、Mac OS X Look And Feel Swing に関する情報が読めます。 ■I got it!やたら大きい段ボール箱に入ってきたので、何だこれは?と、思ったら、すかすかの箱にとっても薄いパッケージが入っていました。 さっそくインストール、、、が、手こずりました。mkino は、前に Mac OS X Server を入れていたので、その上に上書きしたんですがそれがいけなかったらしい。ファイルはコピーされたようだが、再起動すると、Mac OS X Server の起動画面が出て、しばらく固まった後、Open Firmware に落ちてしまう。うーむ、firmware はアップデートされないのか?結局、フォーマットして Mac OS 9 から入れなおしましたとさ。 入れた環境は、
推奨環境はメモリ 128MB 以上だったのでどうなるかなー、と思ってたけど、とりあえず動きました。アプリの起動が遅い気がするけど、比較対象がないからよく分からない。 アプリでちょっと遊んでみましたが、第一印象はドックは素晴らしい!いや、使いやすいかどうかは分からないけど、G3 250 でもなんのストレスもなくマグニファイが行われる。ジニーアニメーション(はくしょん大魔王)もするする動く!触っているだけで楽しいわ、これ。 気になる開発環境ですけど、って普通の人は気にしないか?cc はなし。予想通り。ふーん、これじゃ遊べないなぁ、ぶつぶつ。と、思って何の気なしに Terminal から javac を打ったら、おぉっ、あるじゃん。Java 関係は JDK 相当が一通り入っているようだ。こ、これなら遊べるぞ!明日から Mac OS X de Java だ! ちなみに Java Runtime のバージョンは 1.2.2。でも、Java Hotspot client は 1.3 mixed mode って書いてある。これってどういうこと? ■Darwin 解体新書 - Thread System Call - thread_terminate() - terminate_empty_act()Associated Files:
|
kern_return_t
terminate_empty_act( thread_act_t thr_act ) {
/*
* remove the activation from the pool port
* (this prevents it from being used)
*/
if (thr_act->pool_port) {
act_locked_act_set_thread_pool(thr_act, IP_NULL);
}
/*
* disassociate the thread from the task
*/
assert(thr_act->active);
act_disable_task_locked( thr_act );
return KERN_SUCCESS;
}
|
2 つの関数を呼び出している。まず、act_locked_act_set_thread_pool() (line: 1754)。この関数は、スレッドプールに act_thread を関連づけるためのもの。しかし、いまここで使っているように、スレッドプールの引き数に NULL を指定すると、act_thread を関連しているスレッドプールから削除する、という働きをするのだ!ちゃんと、コメントに書いてあったよ。う〜ん、コメントは重要だねぇ。
もう 1 つは act_disable_task_locked() (line: 2088)。これはタスクから act_thread を削除するもの。こうやって、徐々に中断される準備をしていくんだね。
まとめると、
の 2 つだ。
個人的なことですが、Apple から Mac OS X Public beta を発送したと、今朝メールが来ました。3 日ぐらいだから、水曜か木曜には来るかな?
何からやろうかな〜。どんなのでもいいから、コンパイラが手に入ると嬉しいんだけどな。
まだまだ続くよー。スレッドを終了するための、thread_terminate だ (line: 126, thread_act.c):
kern_return_t
thread_terminate(
register thread_act_t thr_act)
{
thread_t thread;
task_t task;
struct ipc_port *iplock;
kern_return_t ret = KERN_INVALID_ARGUMENT;
...
/*
* Break IPC control over the thread.
*/
ipc_thr_act_disable_act_locked(thr_act);
...
/*
* Check for terminating an empty thread_act -- detach it from
* its task, then deallocate its remaining refs here.
*/
if (thr_act->thread == THREAD_NULL) {
iplock = thr_act->pool_port; /* remember for unlock call */
ret = terminate_empty_act( thr_act );
/*
* Release locks individually. (`act_unlock_thread()'
* will not work since `pool_port' field has changed.)
*/
ip_unlock(iplock);
act_unlock(thr_act);
}
else {
assert(thr_act->active);
act_disable_task_locked(thr_act);
ret = act_abort(thr_act,FALSE);
act_unlock_thread(thr_act);
}
...
return(ret);
}
|
定型的な処理が多いんで、ざっくり省略してみた。注目した箇所は 2 箇所。変数の宣言の後には、引き数のチェックとタスクのロックが続いているけど、まず注目してみたのは IPC を停止しているところ。ipc_thr_act_disable_act_locked() ( line: 327, xnu-1-1/osfmk/kern/ipc_tt.c) を呼び出している。
もう一つは thr_act の thread によって処理を変えているところ。THREAD_NULL の場合は terminate_empty_act() を呼び出して、そうじゃない場合、つまり空じゃない場合は act_disabletask_locked() を呼び出す。実際の処理はこの中だな。
じゃあ、引き続いて thread_create_shuttle() をいってみよう。/xnu-1-1/osfmk/kern/thread.c の 783 行目から:
kern_return_t
thread_create_shuttle(
thread_act_t thr_act,
sp_attributes_t attributes,
void (*start_at)(void),
thread_t *new_thread)
{
thread_t new_shuttle;
task_t parent_task = thr_act->task;
processor_set_t pset;
kern_return_t result;
sched_policy_t *policy;
sf_return_t sfr;
int suspcnt;
/*
* Allocate a thread and initialize static fields
*/
new_shuttle = (thread_t)zalloc(thread_shuttle_zone);
if (new_shuttle == THREAD_NULL)
return (KERN_RESOURCE_SHORTAGE);
*new_shuttle = thr_sh_template;
/* Allocate space for scheduling information and attributes */
/*** Think about integrating with shuttle structure someday ***/
new_shuttle->sp_info = (sp_info_t)kalloc(max_sched_info_size);
if (new_shuttle->sp_info == SP_INFO_NULL) {
zfree(thread_shuttle_zone, (vm_offset_t)new_shuttle);
return (KERN_RESOURCE_SHORTAGE);
}
new_shuttle->pending_sched_attr =
(sp_attributes_t)kalloc(max_sched_attributes_size);
if (new_shuttle->pending_sched_attr == SP_ATTRIBUTES_NULL) {
kfree((vm_offset_t)new_shuttle->sp_info,
(vm_size_t)max_sched_info_size);
zfree(thread_shuttle_zone, (vm_offset_t)new_shuttle);
return (KERN_RESOURCE_SHORTAGE);
}
|
まずは、メモリの確保。new_shuttle、sp_info、new_pending_sched_attr のメモリを確保して、返り値をチェックしている。ちょっと面白いのは、new_shuttle を確保した後、thr_sh_template をコピーして初期化していること。へぇー、テンプレートがあるんだ。
次はロックなどがあって、マシン依存の初期化 (line: 839):
/*
* Initialize system-dependent part.
*/
result = thread_machine_create(new_shuttle, thr_act, start_at);
|
こうやって、マシン依存の箇所をきっちりまとめているのが Mach の特徴だね。
その後に、様々な runtime の初期化が続く (line: 862):
/*
* Initialize runtime-dependent fields
*/
thread_timer_setup(new_shuttle);
machine_kernel_stack_init(new_shuttle, (void (*)(void))thread_continue);
ipc_thread_init(new_shuttle);
thread_start(new_shuttle, start_at);
pset = parent_task->processor_set;
...
if (attributes == SP_ATTRIBUTES_NULL)
attributes = parent_task->sp_attributes;
/* Associate the thread with that scheduling policy */
new_shuttle->policy = attributes->policy_id;
policy = &sched_policy[new_shuttle->policy];
sfr = policy->sp_ops.sp_thread_attach(policy, new_shuttle);
...
/* Indicate that no change in scheduling policy is pending */
new_shuttle->pending_policy = POLICY_NULL;
/* Associate the thread with the processor set */
sfr = policy->sp_ops.sp_thread_processor_set(policy, new_shuttle, pset);
...
/* Set the thread's scheduling parameters */
sfr = policy->sp_ops.sp_thread_set(policy, new_shuttle, attributes);
...
new_shuttle->active = TRUE;
...
*new_thread = new_shuttle;
...
return (KERN_SUCCESS);
}
|
まとめると、
といった感じかな。各種エラー処理は省いておいたよ。大体、タスクで設定する項目と一緒だよね。
Apple のページ It's Tool Time! に Mac OS X の開発環境に関する案内が出ている。それをまとめると、
と、いうことは、一般の趣味プログラマが開発環境を手に入れるには、$400 払って来週 CD をもらうか、一月待ってダウンロードするか、ということらしい。
mkino はとりあえず Public beta だけ手に入れて、開発環境はダウンロードするとします。たぶん、Public beta には cc はついてこないんでしょう。NeXT も初期状態では入ってなかったようだし。そうかい、そうかい、ふーんだ、、、(ちょっとすねてる)
でも、たぶん、Java の Runtime は入っているんだよね。じゃあ、javac が入っていたりするかな、、、?たぶん、無理か(この文章、たぶん、が多い)。
Today's BGM: 羊の歩み from GOLDBLEND by 奥田民夫
“ほんとにこの世に神様が いーるーならーばー
のんべんだらりの僕を見ーてー いーるーならーばー なんか言ってよぉぉぉ〜”
というわけで、active なスレッドを作るための関数、act_create() だっ!/xnu-1-1/osfmk/kern/thread_act.c の 1090 行目だ:
kern_return_t
act_create(task_t task,
thread_act_params_t params,
thread_act_t *new_act)
{
thread_act_t thr_act;
int rc;
vm_map_t map;
thr_act = (thread_act_t)zalloc(thr_act_zone);
if (thr_act == 0)
return(KERN_RESOURCE_SHORTAGE);
...
/* Start by zeroing everything; then init non-zero items only */
bzero((char *)thr_act, sizeof(*thr_act));
|
引き数は、親となるタスクの task、パラメタの params、返り値となる作られた thread_act_t が入る new_act の 3 つだ。
始めにやることはメモリの確保だ。お約束だね。bzero っていうのは /xnu-1-1/osfmk/kern/misc_protos.h に宣言がある。渡された領域を 0 で埋めるらしい。
次は 1112 行目から:
/* Start with one reference for being active, another for the caller */
act_lock_init(thr_act);
thr_act->ref_count = 2;
/* Latch onto the task. */
thr_act->task = task;
task_reference(task);
thr_act->active = 1;
/* Initialize sigbufp for High-Watermark buffer allocation */
thr_act->r_sigbufp = (routine_descriptor_t) &thr_act->r_sigbuf;
thr_act->r_sigbuf_size = sizeof(thr_act->r_sigbuf);
|
やっていることは、参照回数の設定。thr_act の参照回数を初期値の 2 に設定。次に出てくる task_reference() は /xnu-1-1/osfmk/kern/task.c に定義がある関数で、task の参照回数を増やしている。r_sigbufp と r_sigbuf_size は RPC 関係。
次はスタックの初期化 (line: 1125):
/* Initialize thread stack */
if (params)
{
thr_act->user_stack = params->stack;
thr_act->user_stack_size = params->stack_size;
thr_act->user_entry = params->entry_func;
}
else
{
thr_act->user_stack = 0;
thr_act->user_stack_size = 0;
thr_act->user_entry = 0;
}
|
スレッドの初期化の仕方は、この関数の引き数 params によって変わるらしい。params が NULL でないときは、user_stack、user_stack_size、user_entry がそれぞれ設定される。そうでないときは全部 0。thread_create() から呼び出されるときは、NULL が渡されている。
次はもろもろの初期化 (line: 1159):
/* Initialize the held_ulocks queue as empty */
queue_init(&thr_act->held_ulocks);
/* Inherit the profiling status of the parent task */
act_prof_init(thr_act, task);
ipc_thr_act_init(task, thr_act);
act_machine_create(task, thr_act);
|
キューの初期化、プロファイラの初期化、IPC (Inter Process Call) の初期化と続く。ちょっと面白いのが act_machine_create()。マシン依存関係の初期化だよね。これは後で見てみよう。
ちょっと飛ばして、次はタスクのキューにこのスレッドを追加する (line: 1185):
task_lock(task);
/* Chain the thr_act onto the task's list */
mutex_lock(&task->act_list_lock);
queue_enter(&task->thr_acts, thr_act, thread_act_t, thr_acts);
task->thr_act_count++;
/* no need to check for transition from 0 -> 1 here */
task->res_act_count++;
mutex_unlock(&task->act_list_lock);
task_unlock(task);
|
task->thr_acts にこのスレッドを追加するわけだ。その後で、task->thr_act_count を増やしておく。
んで、最後にメモリ関連 (line: 1197):
/* Cache the task's map and take a reference to it */
thr_act->map = task->map;
/* Inline vm_map_reference cause we don't want to increment res_count */
map = task->map;
mutex_lock(&map->s_lock);
...
map->ref_count++;
mutex_unlock(&map->s_lock);
*new_act = thr_act;
return KERN_SUCCESS;
}
|
このスレッドに、タスクの map を設定しておく。キャッシュのため、ってコメントに書いてあるよね。別にこれがなくても、タスク経由で毎回アクセスしても変わらないんだけど、スピードを上げるため、こっちでも持っておくわけだ。その後、map の ref_count を増やす。コメントに `res_count' は増やさない、って書いてあるけど、ref_count と res_count ってどう違うんだ?
一番最後に、引き数 new_act に作ったスレッドを設定してやって、おしまい。
恐らくみなさんご承知の通り、Mac OS X public beta が公約通り「夏」に発表されました。まぁ、まだサマータイムだしね。夏っちゃあ、夏か。
Apple Store で注文できて、約 $30。この、パブリックベータを売るっていう姿勢はどうなんだろうね。もともとベータ版は、広く意見を聞いたりバグフィックスを行うためのもの。できるだけ多くの人に使ってもらうには、無料にするのがてっとり早い、っていうのがベータ版のもともとの考え方だったんでしょう。Apple の場合は、有料にしても充分な数の申し込みが来ると踏んでいたんでしょうか?つくづく Apple は幸せな会社です。そして、それだけの期待を集めている開発グループのプレッシャーは測り知れないでしょう。「真に新しいもの」を作る力、ひらめきは、血ヘドを吐き、アイデンティティをけずりながらでないと産まれてこないのですから。
ともあれ、早速申し込みました。朝はすごく混んでいてつながりにくかったですが、一時間ほどトライしてようやく受理された模様。三日から一週間で届く、と書いてありましたから、来週の頭ぐらいにはインストールできるでしょうか。
こつこつ、こつこつ、いつ終わるともしれず、ひたすらコードを読むぅ〜。
気がついたら 1,000 アクセスを超えていました。Thanks folks! しっかし、誰かの役に立つのかな、このサイト?
今日からスレッドのシステムコールに入ります。まずは、thread_create() から〜。/xnu-1-1/osfmk/kern/thread.cpp にあるよ。
ざっと眺めてみると、スレッドの生成は、
という流れになってるようだね。
ま、順々に見ていきますか。とりあえず、コードを下に乗せておくね (line: 960):
kern_return_t
thread_create(
task_t task,
thread_act_t *new_act)
{
thread_act_t thr_act;
thread_t thread;
kern_return_t result;
sched_policy_t *policy;
sf_return_t sfr;
spl_t s;
extern void thread_bootstrap_return(void);
result = act_create(task, NULL_PARAMS, &thr_act);
if (result != KERN_SUCCESS)
return (result);
result = thread_create_shuttle(thr_act, SP_ATTRIBUTES_NULL,
thread_bootstrap_return, &thread);
if (result != KERN_SUCCESS) {
thread_terminate(thr_act);
act_deallocate(thr_act);
return (result);
}
if (task->kernel_loaded)
thread_user_to_kernel(thread);
/* Start the thread running (it will immediately suspend itself). */
s = splsched();
thread_ast_set(thr_act, AST_APC);
thread_lock(thread);
thread->state |= TH_RUN; /*** ??? I think this is okay ***/
/* Allow the thread to execute */
policy = &sched_policy[thread->policy];
sfr = policy->sp_ops.sp_thread_dispatch(policy, thread);
if (sfr != SF_SUCCESS)
panic("thread_create: sp_thread_dispatch");
thread_unlock(thread);
splx(s);
...
*new_act = thr_act;
return (KERN_SUCCESS);
}
|
Mac OS Rumors によれば、Mac OS X public beta はネットワークダウンロード可能なイメージで提供されるとのこと。サイズは 300MB 程度。
うそっ?ほんとかよー?いままでの Mac OS X って、CD からブートしないとインストールできなかったよね?いや、mkino は Mac OS X Server しかいじってないから、よく分からないけど。あれは、インストールの最中に何度も再起動してわずらわしかった。もし、ハードディスク上のイメージからインストールできるならば、インストールプロセスはだいぶ改善された、っていうことだ。
さらに一歩踏み込んで、そのうちネットワークインストールもできるようになるのかな?それが時代の流れでしょう。
もう一つの thread を管理するための構造体、thread_shuttle をしらべよう。 /xnu-1-1/osfmk/kern/thread.h の 165 行目だ:
typedef struct thread_shuttle {
/*
* Beginning of thread_shuttle proper. When the thread is on
* a wait queue, these three fields are in treated as an un-
* official union with a wait_queue_element. If you change
* these, you must change that definition as well.
*/
queue_chain_t links; /* current run/wait queue links */
run_queue_t runq; /* run queue p is on SEE BELOW */
int whichq; /* which queue level p is on */
...
/* Thread bookkeeping */
queue_chain_t pset_threads; /* list of all shuttles in proc set */
|
最初の 3 つはよくわかんない。ごめん。下にいって、pset_threads は thread_shuttle のリストらしい。
次はスレッドの状態を保存しておくための変数 (line: 186):
/* Self-preservation */
decl_simple_lock_data(,lock) /* scheduling lock (thread_lock()) */
decl_simple_lock_data(,wake_lock) /* covers wake_active (wake_lock())*/
decl_mutex_data(,rpc_lock) /* RPC lock (rpc_lock()) */
int ref_count; /* number of references to me */
vm_offset_t kernel_stack; /* accurate only if the thread is
not swapped and not executing */
vm_offset_t stack_privilege;/* reserved kernel stack */
|
最初の 3 つ、lock、wake_lcok、rpc_lock は、排他制御のための mutex。ref_count は参照回数。次はスタック関連。kernel_stack はカーネルのスタックのオフセット(?)。スレッドがスワップしたり、実行されないときにだけ保証されるらしい。stack_privilege は特権モードの時のスタックのオフセットか?
次はブロックするための情報 (line: 141):
/* Blocking information */
int reason; /* why we blocked */
event_t wait_event; /* event we are waiting on */
kern_return_t wait_result; /* outcome of wait -
may be examined by this thread
WITHOUT locking */
wait_queue_t wait_queue; /* wait queue we are currently on */
queue_chain_t wait_link; /* event's wait queue link */
boolean_t wake_active; /* Someone is waiting for this
thread to become suspended */
int state; /* Thread state: */
boolean_t preempt; /* Thread is undergoing preemption */
|
最初の reason は、なぜブロックされるかの理由が入る。どこかの定数が入るんでしょう。wait_event は、待っているイベントが入る。このイベントがスレッドに届いたら、スレッドは動き始めるわけだ。wait_result はウェイト状態に入るときに呼び出される関数の返り値かな?
wait_queue はウェイト状態になっているスレッドのキュー。wait_link はイベントのキュー。wake_active はだれかがこのスレッドを待っているかを示す(ごめん、良く分からん)。
state はスレッドの状態。preempt はプリエンプティングをするかどうかを決める。プリエンプトじゃないスレッドもできるんだ。
スレッドの状態は、218 行目から定義されている:
同時に複数の状態をとれるようになっているみたい。
その後もいろいろな変数が続くけど、かなり分かりにくい。スレッドのシステムコールを読むときにまた戻ってくるとします。
ここまでで予想できるのは thread_activation はアクティブ状態(教科書だと励起状態とか書いてあるんだよな)の情報を、thread_shuttle は中断状態の情報を管理するためのもののようだ。
thread を管理するための構造体の一つ、thread_activation は /xnu-1-1/osfmk/kern/thread_act.h の 115 行目にある:
typedef struct thread_activation {
/*** task linkage ***/
/* Links for task's circular list of activations. The activation
* is only on the task's activation list while active. Must be
* first.
*/
queue_chain_t thr_acts;
/* Indicators for whether this activation is in the midst of
* resuming or has already been resumed in a kernel-loaded
* task -- these flags are basically for quick access to
* this information.
*/
boolean_t kernel_loaded; /* running in kernel-loaded task */
boolean_t kernel_loading; /* about to run kernel-loaded */
|
最初の変数 thr_acts は、アクティブなスレッドのリストだ(?)。ごめん、よく分からない。あとで調べておく。次のフラグ kernel_loaded と kernel_loading は、このスレッドはカーネルにロードされているか、カーネルにロードされている最中かを示す。
次いこう (line: 133):
/*** Machine-dependent state ***/
struct MachineThrAct mact;
/*** Consistency ***/
decl_mutex_data(,lock)
decl_simple_lock_data(,sched_lock)
int ref_count;
|
おぉっ!ここにきて始めてマシン依存のコードが出てきた。構造体 MachinThrAct に、マシン(ていうかチップ)依存のコードが集められている。ここさえ返れば、Darwin を他のマシンに移植できるってわけだ(ここだけじゃなくて、もっといっぱいあるけどさ)。その次の Consistency は排他制御のための mutex 等。
続いてタスクとメモリ関連 (line: 141):
/* Reference to the task this activation is in.
* Constant for the life of the activation
*/
struct task *task;
vm_map_t map; /* cached current map */
|
task は、当然このスレッドを所有しているタスク。map はこのスレッド(および親のタスク)のアドレス空間。
続いてポート (line: 147):
/*** thread_pool-related stuff ***/
/* Port containing the thread_pool this activation normally lives
* on, zero if none. The port (really the thread_pool) holds a
* reference to the activation as long as this is nonzero (even when
* the activation isn't actually on the thread_pool's list).
*/
struct ipc_port *pool_port;
/* Link on the thread_pool's list of activations.
* The activation is only actually on the thread_pool's list
* (and hence this is valid) when not in use (thread == 0) and
* not suspended (suspend_count == 0).
*/
struct thread_activation *thread_pool_next;
|
このポート pool_port は thread_pool と通信するためのポートらしい。じゃあ、thread_pool とは何か?あとで調べておこう。thread_pool_next は、thread_pool における、次のスレッドへのポインタらしい。
次スタック (line: 162):
/* User stack location and size for initialization on migrating RPCs */
vm_offset_t user_stack;
vm_size_t user_stack_size;
/*
* Entry point for upcall w/o registered subsystem and pointer
* to return code
*/
entry_function_t user_entry;
|
user_stack はスタックのオフセット。user_stack_size はスタックのサイズ。user_entry はスレッドがスタートしたときに呼び出される関数へのポインタ。この辺が、普通にスレッドを作るとき、ユーザが指定するところだよね。
次は RPC (Remote Procedure Call) (line: 172):
/* RPC state */
union {
struct {
rpc_subsystem_t r_subsystem;
rpc_id_t r_routine_num;
rpc_signature_t r_sig_ptr; /* Stored Client Sig Ptr */
rpc_size_t r_sig_size; /* Size of Sig stored */
struct rpc_signature r_sigbuf; /* Static Reservation of Sig Mem */
routine_descriptor_t r_sigbufp; /* For dynamic storage of Sig */
vm_size_t r_sigbuf_size; /* Size of buffer allocated for sig */
vm_offset_t r_new_argv;
vm_offset_t *r_arg_buf;
vm_offset_t r_arg_buf_data[RPC_KBUF_SIZE];
rpc_copy_state_t r_state;
rpc_copy_state_data_t r_state_data[RPC_DESC_COUNT];
unsigned int r_port_flags;
ipc_port_t r_local_port;
void *r_kkt_args;
} regular;
struct {
ipc_port_t r_port;
ipc_port_t r_exc_port;
int r_exc_flavor;
mach_msg_type_number_t r_ostate_cnt;
exception_data_type_t r_code[EXCEPTION_CODE_MAX];
} exception;
} rpc_state;
|
ここでは union の rpc_state が定義されている。struct regular か struct exception の状態を取るわけだ。たぶん、regular は通常の呼び出しで、exception は例外が発生したときの呼び出しだな(当たり前か)。詳しい中身は RPC の項で別途取り上げよう。
次はスレッドのリンク (line: 209):
/*** Thread linkage ***/
/* Shuttle using this activation, zero if not in use. The shuttle
* holds a reference on the activation while this is nonzero.
*/
struct thread_shuttle *thread;
/* The rest in this section is only valid when thread is nonzero. */
/* Next higher and next lower activation on the thread's activation
* stack. For a topmost activation or the null_act, higher is
* undefined. The bottommost activation is always the null_act.
*/
struct thread_activation *higher, *lower;
/* Alert bits pending at this activation; some of them may have
* propagated from lower activations.
*/
unsigned alerts;
/* Mask of alert bits to be allowed to pass through from lower levels.
*/
unsigned alert_mask;
|
thread は、もう一つのスレッド関連の構造体 thread_shuttle へのポインタ。higher と lower は、次のスレッドのスタックの high と low。これって必要なんだろうか?自分の high と low はどうするんだろう?ちょっと疑問。alerts と alert_mask はアラートのためのものでしょう。
続いて、制御情報 (line: 244):
/*** Control information ***/
/* Number of outstanding suspensions on this activation. */
int suspend_count;
/* User-visible scheduling state */
int user_stop_count; /* outstanding stops */
/* ast is needed - see ast.h */
int ast;
|
suspend_count はスレッドの中断で使われるんだよね。user_stop_count はなんだろう?そして ast は?ast.h を見ると、Asynchronous System Traps(同期システムトラップ?)のことらしい。
そして後は、その他もろもろの変数が続く (line: 269):
/* This is normally true, but is set to false when the
* activation is terminated.
*/
int active;
/* Chain of return handlers to be called before the thread is
* allowed to return to this invocation
*/
ReturnHandler *handlers;
/* A special ReturnHandler attached to the above chain to
* handle suspension and such
*/
ReturnHandler special_handler;
/* Special ports attached to this activation */
struct ipc_port *ith_self; /* not a right, doesn't hold ref */
struct ipc_port *ith_sself; /* a send right */
struct exception_action exc_actions[EXC_TYPES_COUNT];
/* A list of ulocks (a lock set element) currently held by the thread
*/
queue_head_t held_ulocks;
...
} Thread_Activation;
|
スレッドが終わる直前に呼び出す handler と、いくつかの特別なポートだ。
こんなとこでしょうか。
MacFixIt 等によれば、Apple が Mac OS X public beta の配付に関してアナウンスを出しているとのこと。いわく「9/13 に Mac OS X のサイトにくれば詳しい案内がありますよ。」
たぶん、ここで申し込み、後日郵送ということなんでしょう。もっとすっぱり言わんかい!日本はどうなるの?というのはあいかわらず謎のまま。
とりあえず、あと 5 日。手にできるのは一週間後ぐらいかな?わくわくしながら、Apple のことだから土壇場で裏切るかもしれませんが、待ちましょう。
スレッドの中身を見てみるとしよう。まずは、スレッド構造体から、、、と思ってソースコードを眺めていると、thread_t は直接スレッドのすべての情報を保持しているわけではないらしい。じゃあ、どーなってんの?と思っていろいろ見てると、面白いコメントを見つけた。/xnu-1-1/osfmk/mach/i386/i386-mach.defs の 39 行目:
The infamous name change: thread_activation + thread_shuttle = thread.
(悪名高い名前の変更)
なるほど。thread_activation と thread_shuttle を足したものが thread になるわけね。これは確かに分かりにくい。このコメントを書いた人も怒っていたんだろう。
構造体 thread_activation_t の定義は /xnu-1-1/osfmk/kern/thread_act.h に、構造体 thread_shuttle_t の定義は /xnu-1-1/osfmk/kern/thread.h にある。次回はこいつらからだな。
えくしゅっ!寒いよ。風邪気味だよ。夏休みを終えて戻ってきたら、完全に秋だね。こんなに涼しくなっているとは思わなかったから、不意打ちをくらってしまったよ。やっぱり砂漠なんだな、ここ、、、
さて、今日からスレッドの方を見てみよう。プロセスの方もまだ残っているコールがあるけど、また戻るから。
まずはシステムコールから。/xnu-1-1/osfmk/mach/task.def と /xnu-1-1/osfmk/mach/thread_act/def に定義がある。task.def の方には、thread_create() と thread_create_running() がある。それ以外は thread_act.def の方だ。
| thread_create | 新しいスレッドを作る |
| thread_create_running | 新しいスレッドを状態を指定して作る |
| thread_terminate | スレッドを破壊する |
| thread_get_state | スレッドの状態を返す |
| thread_set_state | スレッドの状態を設定する |
| thread_suspend | スレッドを中断する |
| thread_resume | スレッドを再開する |
| thread_abort | スレッドを停止する |
| thread_get_special_port | 指定されたポートを返す |
| thread_set_special_port | 指定されたポートを設定する |
| thread_info | スレッドの情報を返す |
| thread_set_exception_ports | スレッドに例外ポートを設定する |
| thread_get_exception_ports | スレッドの例外ポートを返す |
| thread_policy | スレッドにスケジュールポリシーを設定する |
| thread_set_sched | スレッドにスケジュールポリシーを設定する |
| thread_get_sched | スレッドのスケジュールポリシーを返す |
| thread_assign | スレッドにプロセッサを割り当てる |
少し省いてあります。タスクのシステムコールと比べると、非常に似通っていることが分かるよね。タスクのコールの本質は、それらに属しているスレッドのコールを呼び出すことにあるからだ。
なつやすみを終えて帰ってきました。やー、あそんだ、あそんだ。ひたすら飲み歩いていました。
休んでいる間に、Mac OS X public beta の配付日程が決まっていましたね。9/13 かぁ。あと一週間ちょっと。うーん、待ち遠しい!
気になるのは、配付方法。郵送なのか?ショップでばらまくのか?雑誌に添付?たぶん、サイトに申し込んで、CD 郵送だろうね。日本と US でタイムラグはでるのか?そもそも日本語版って用意されるのか?個人的には US で申し込むから関係ないんだけど。
もうひとつ気になるのは開発環境。掲示板等のうわさでは、一般向けでは Project Builder と Interface Builder *1 が付属しないらしい。じゃあ、エディタと gcc *2、javac *3 でできるのだろうか?でも、根強いうわさとして、beta には Terminal がつかないっていうのもあるよね。それじゃ開発できないじゃん!結論はもうすぐでるけど、Apple が日曜プログラマの道をとざさないことを祈りたいよね。
*1) NeXT に付属していた開発環境。とくに、Interface Builder は GUI ベースで GUI アプリが開発できるということで(へんな言い回し)、衝撃的だった。現在の RAD ツールの始祖みたいなものだよ。
*2) C コンパイラ。C、C++、Objective-C がコンパイル可能。これがあれば Aqua ベースのアプリが作れる。
|
Home | Link | Download | Back Number | Speciall Issue
|