2026-04-09

rkubootのbufは、もうひとつ欲しい

SIMHのPDP-11エミュレータを使ってUNIXv6について調べています。RK05にインストールされたカーネルを起動するには、ディスクの先頭にあるブートローダ「rkuboot」が使われています。これの主な処理は「fsboot.s」にあります。これはファイルシステムを解釈して、指定されたカーネルのiノードを取得し、その中で参照されているディスクブロックをメモリ上にロードします。UNIXv6のカーネルは、今の目から見れば小さいですが、さすがに直接参照だけでは足らないので、間接参照が使われています。

 

処理の概要は、間接参照のディスクブロックをひとつずつ見ていき、その指し示しているディスクブロックを順番にメモリ上に持ってきます。ここで気になるのが、間接参照のディスクブロックを見る際も、カーネルに相当するディスクブロックを得る際も、512バイト分として確保されている領域「buf」を使っていることです。

 

カーネルに対応するiノードは、メモリ上に確保された「inod」に置かれますから、特に問題はありません。そのiノードの中から間接参照によるディスクブロック番号を得るために、「buf」を使用します。そしてカーネルの一部があるディスクブロック番号が判明すると、その内容を「buf」に持ってくるのですが、こうすることで、先ほどの間接参照として使っていた内容は上書きされてしまいます。「buf」にあるのはカーネルの一部ですから、それをメモリのしかるべき位置にコピーしています。カーネルの次のディスクブロックを得るには、再びiノードを参照して間接参照によるディスクブロック番号を取得する必要がありますが、「buf」には情報がありませんから、またしてもrmblkで読み込む必要があります。この繰り返しが、カーネル全体を読み込むまで続きます。

 

ひとつの「buf」を使いまわしているから、こうなってしまうのではないかと思います。UNIXv6当時のCPUやメモリリソースが限られていたとしても、「buf」ひとつだけで処理しないで、もうひとつあれば良いのにと思うところです。 

rkubootにおける直接参照も間接参照も、カーネルをブートできれば良いと割り切っている

SIMHのPDP-11エミュレータを使いUNIXv6について学んでみようと思っています。マシンが起動され、RK05にインストールされたカーネルがメモリ上にロードされるには、ディスクの先頭にあるブートローダ「rkuboot」が使われます。この主処理は「fsboot.s」にあります。このローダがファイルシステムを解釈して、カーネルを探して、メモリ上に配置し、制御を移すことで、カーネルが動き出します。

 

ファイルシステムを解釈するには、iノード、ディスクブロックの直接参照や間接参照などを取り扱う必要があります。その処理はルーチン「rmblk」にあるようです。UNIXv6当時のファイルシステムは、今日とは異なり、「LARGEフラグ」をみて、直接参照と間接参照を切り替えていたようです。さらにコメントには「huge algorithm is not implemented」とあり、二重間接参照は実装していません。

 

ブートローダの目的は、UNIXのファイルシステムを正確に取り扱うことではなく、カーネルさえ読み込めれば十分であるという割り切りがあるようです。そうであるなら、当時のカーネルは小さなファイルだったので、さすがに直接参照では扱えませんが、間接参照だけで読み込める程度の大きさだったのでしょう。二重間接参照が必要となるほど大きなファイルにはならなかったので、それを実装したところで絶対要らないのが明らかですから、余計なことはしていないのでしょう。

 

しかも、直接参照にしろ間接参照にしろ、ディスブロックとして得られた値が「0」になっていれば、そこで読込みを終えることになります。そのためのロジックは入っていますし、「bno = buf+514.」のように、何故か512+2が指定されていることからわかるように、それなりに考慮はされているようです。しかし、直接参照にしろ間接参照にしろ、いかなる場合も絶対に問題が起きないように念入りに対処するというよりは、「カーネルを読むだけだから、細かいところは気にしなくても良いだろう」と割り切っているのではないかと思われるロジックに見えます。

 

ブートローダは512バイト以下におさめる必要がありますから、エラーチェックをしっかりとか、ファイルシステムを完璧に実装しようなどという理想に走ると、容量をオーバーしてしまうでしょう。問題があることはわかっているけど、仕方ないよねという割り切りを感じるのです。 

リッチーとトンプソンによる「The UNIX Time-Sharing System」は2つある

PDP-11で動作したUNIXv6について学ぼうとすると、真っ先に挙げられるのは「Lions本」と呼ばれる『Lions' Commentary on UNIX』です。僕自身も、アスキー出版局から出た和訳を参考にしています。その中には、次のような箇所があります。

  • 『The UNIX Time-sharing System』はオリジナルの『Communications of the ACM』の論文の改訂版である。1か月に少なくとも一度は再読すべきである。

  

この一文の前半にある「論文の改訂版である」というのは、一読しても何のことか分かりませんが、後半の「再読すべきである」は、よく分かります。暗記してしまうくらい熟読せよという意味でしょう。これはネットを検索すれば見つかるので、手元に置いておき、仰せの通りに何度も読み直そうと思います。

 

UNIXv6のブートローダを調べていると、ファイルシステムの構造として「LARGEフラグ」というものがあり、それに応じて、直接参照と間接参照を切り替えていることを知りました。今日でもファイルシステムでは直接参照と間接参照がありますが、「LARGEフラグ」というものは存在しません。何時ごろ無くなったのだろうかとGeminiに尋ねてみたら、UNIXv7で変わったとの回答を得ました。ということは、LARGEフラグがあるのはUNIXv6が最後ということになります。

 

しかもGeminiがいうには、「The UNIX Time-Sharing System」には、1974年のCACM版と、1978年のBSTJ版があるそうです。その「4 Implementation of the file system」の記述内容が大きく変わっているとのことでした。あらためてネットから入手し直してみると、タイトルも著者も同じですが、内容が異なる2つの論文があることを確認しました。

 

UNIXの系統樹などを見ると、BSD版はUNIXv6から派生したように書かれています。直接的にはそうなのかもしれませんが、UNIXv7で加えられた変更をBSDが取り込んだタイミングがあると思います。それは何時だったのでしょうか。BSDの旧いソースを追いかけると、わかるかもしれません。 

rmblkはリターンアドレスを操作する

SIMHのPDP-11エミュレータを利用し、RK05にインストールされたUNIXv6のブートローダ「rkuboot」について調べています。これは「run」により作られますが、主たる処理は「fsboot.s」にあります。この中にあるルーチン「rmblk」は、サブルーチン呼び出しのリターンアドレスを操作しているようです。

 

rmblkが呼び出されると、まず「add $2,(sp)」があります。このままサブルーチンを終える場合もありますが、場合によっては、戻る直前に「sub $2,(sp)」しています。一方でrmblkの呼び出し元では「jsr pc,rmblk」の直後に「br callout」と「mov $buf,r1」が置かれています。普通なら、JSR命令で呼び出したサブルーチンから戻ってくると、その次の命令(ここであれば、BR命令)が実行されることになります。しかしrmblk側でリターンアドレスを操作しているので、戻り方に依っては命令を飛ばし(BR命令は実行されず) 、MOV命令から実行されます。かなり職人芸と言える技だと思います。

 

PDP-11上でUNIXv6が動作していた時代は、現在から比べると、CPU性能もメモリ容量も極めて限られていたので、このような芸当を駆使していたのだと説明されることがあります。確かにその通りかもしれません。そうであったとしても、このような芸当に走るのは、ちょっとやりすぎではないかと思わないでもありません。rmblkが正常終了したか異常終了したかに依って処理を分岐させたいのであれば、他の方法もあるだろうし、そうすることが困難だとも思えません。 

rkubootにはmbootのような行入力の特殊文字がない

SIMHのPDP-11エミュレータを利用し、「SETTING UP UNIX - Sixth Edition」における「Making a Disk From Tape」から「Booting UNIX」の動作を追体験しています。配布されたテープイメージをディスクにコピーする段階では、テープの先頭にあるmbootにより、ディスクからブートする段階では、ディスクの先頭にあるrkubootにより、キー入力された名前のプログラムを探し、それに制御を移しているようです。mbootもrkubootも、Geminiに助けてもらいながら、ロジックの詳細は理解できました。

 

mbootもrkubootもスクリプト「run」で作られます。mbootの中心となるのは「tpboot.s」で、rkubootの中心となるのは「fsboot.s」で、どちらも同じような作りになっています。ただしよく見ると、tpboot.sでは、キー入力を間違った場合の簡易編集機能として「@」や「#」を扱う処理が入っています。しかしfsboot.sにはありません。

 

mbootにしろrkubootにしろ、キー入力時に簡易編集機能が必須というわけではないと思います。あればあったで便利かもしれませんが、なければなくても別に困らないと思います。また簡易編集機能を処理に組み込んだとしても、些細なロジックですから、ブートローダの512バイト制限を気にするほどのことではないと思います。

 

おそらく簡易編集機能は、あってもなくても構わない程度の、おまけ機能なのでしょう。 

2026-04-08

rkubootの動作

SIMHのPDP-11エミュレータを使用してUNIXv6について学んでいます。同様の試みはオンラインでも書籍でも数多く存在していますが、カーネル本体がメモリ上にロードされた以降を対象にしているようです。もちろんそれで何の問題もないのですが、いきなりカーネルを学ぶよりも、助走のつもりで、ディスク上のブートローダがカーネルをメモリに読み込むところから調べてみることにしました。UNIXv6のブートローダは、ディスク毎に分かれているようですが、RK05用のrkubootを対象にします。

 

rkubootはスクリプト「run」で作られています。主要部分は「fsboot.s」です。ブート手順については「SETTING UP UNIX - Sixth Edition」の「Booting UNIX」に書かれているとおりです。まずPDP-11のフロントパネルからブートローダをメモリ上に置くプログラムを入力し、実行します。そして読み込まれたブートローダを実行すると、プロンプト「@」が出力されるので、カーネル名称として(例えば)「rkunix」を入力します。こうするとブートローダがカーネルを読込み、制御を移し、カーネルが動き出します。

 

ブートローダ「rkuboot」の概要は上述したとおりですが、「fsboot.s」を追いかけてみると、次のような処理をしていました。

  1. 0番地に置かれている自分自身を137000番地に移す。こうしておかないと、カーネルを読み込もうとした際に、ブートローダ自身が上書きされて壊れてしまいます。
  2. プロンプト「@」を出力し、カーネルのパス名を入力させまる。ここで、mbootにあったような特殊文字「#」や「@」のハンドリングはありません。またカーネルが階層ディレクトリに置かれている場合でも対処できる処理になっています。
  3. 入力されたカーネル名称をファイルシステムから探します。FILE SYSTEM(V)にも明記されているとおり、i番号の「1」がルートディレクトリなのが決まりですから、それを前提に探します。そしてルートディレクトリ(またはサブディレクトリ)に指定されたカーネルが見つかれば、そのi番号をからi-nodeを参照してディスク上のカーネルをメモリに読み込みます。

 

ブートローダですから、上記した内容が全てですが、これらの処理を512バイト以内に納める必要があります。あまり複雑な処理とか、エラーチェックとかをしている余裕がないようで、ある種の「割り切り」があるように思います。例えば、fsboot.sのコメントには「huge algorithm is not implemented」とありますが、これは今日のUNIXにもある「二重間接参照」は実装されていないということのようです。実際問題として、二重間接参照が必要となるほどカーネルは大きくないので、実装しなくても実用上問題ないという「 割り切り」があるのでしょう。他にもエラーチェックなどをしない箇所がありますが、それも「普通そういう事態にはならないはず」という「割り切り」があるのだと思います。

2026-03-28

「Making a Disk From Tape」と「Booting UNIX」の対称性

SETTING UP UNIX - Sixth Edition」の「Making a Disk From Tape」と「Booting UNIX」は、手順が対称的になっていることに気づきました。

 

「Making a Disk From Tape」では、細部を省略すると、次のような手順です。

  1. TU10コードをフロントパネルから入力して実行する。
  2. mbootがロードされるので、実行されると、プロンプト「=」が出力される。
  3. tmrkなどを入力することで、テープからディスクにコピーが行われる。

 

これに対して「Booting UNIX」では、次のような手順です。

  1. RK05コードをフロントパネルから入力して実行する。
  2. rkubootがロードされ、実行されると、プロンプト「@」が出力される。
  3. rkunixなどを入力することで、ディスクからカーネルが起動する。

 

「Making a Disk From Tape」の処理を追いかけてくることで、UNIXv6が動作する以前の世界が理解できました。