"Howtowriteyourownfilesystem?"  第1回フロッピーディスクを斬るÁ 西田 亙(NISHIDAWataru) UNIXカーネルを魂とすれば、ファイルシステムは魂が宿る肉体です。いかに優秀なカーネルとۗえども、

"Howtowriteyourownfilesystem?"
第1回フロッピーディスクを斬るÁ
西田 亙(NISHIDAWataru)
UNIXカーネルを魂とすれば、ファイルシステムは魂が宿る肉体です。いかに優秀なカーネルとۗえども、
肉体ぬきでこの世に関わることはできません。UNIXのファイルシステムは、System5filesystem
(s5fs)から絶え間ない進化を続けた結果、‫ݗ‬機能化を遂げると同時に、‫ݗ‬度に複ߙ化しました。このた
め、初学者のテーマとしては、少々荷が重すぎるようです。そこで、この連載では最も単純なThe
SimplestFileSystemを自分たちの手で০‫・ڐ‬構築することで、ファイルシステムのエッセンスを理ӕす
ることを目指しています。
■はじめに
かって、KenThompson氏はUNIXの始祖であるUnicsを開発するにあたり、まず最初にChalkfile
systemと呼ばれるシンプルなファイルシステムを実装しました。注目すべきは、この時点ではまだプロセ
ス管理は登場しておらず、ただ単に実行プログラムやデータファイルを格納する目的だけのためにファイ
ルシステムが使われていたという事実です。現代UNIXにおいて、カーネルとファイルシステムは不可分
の関係にありますが、進化学的に見ればファイルシステムの方が遙かに先行していたことになります。そ
こで、私はUNIXカーネルに踏み込む前に、まずファイルシステム単独のӕ説を行うこととしました。こ
れまでUNIXカーネルの学習が困難であった一因は、従来の教科書がプロセス管理とファイルシステムを
同列に扱ってきた点にあると考えたからです。果たして、このアプローチが功を奏するのか、もしくは私
の独りよがりに終わってしまうのか、皆さんの素直なご意見をお聞かせ頂ければ幸いです。
平成15年1月3日 著者記す
■フロッピーディスクを知る
現代のファイルシステムには、FFS,UFS,EXT2,FAT,NFS,ProcFSなど、それこそ星の数ほどの種་
が存在します。一般的にファイルシステムはローカルディスク上に格納されますが、ネットワークサー
バーや、RAM、カーネル内メモリーなど様々な媒体に住み着く(populate)ことが可能です。
私達が作成するSFSの対象は、単純化のためにフロッピーディスクに限定することとしました。理由は
次の通りです。
・サイズが小さい(1440KB)ため、入出力処理が簡単
・フロッピー一枚を用意するだけで、誰もが手軽に実験できる
・フロッピーディスクの内൉構造(ヘッド/トラック/セクター)を、
コーディングを通じて理ӕ可能
・ハードディスク操作に必要な基本知ࡀを修得できる
特に3番目の項目は重要です。誰もが知っているフロッピーディスクですが、その物理フォーマットと
なると一体どれだけの人が把握していることでしょう?確かに、通常のアプリケーションプログラムを
-1-
コーディングしている限り、このような細൉の技術情報を知っている必要はなく、read/write関数を知っ
ているだけで十分です。しかし、ディスクの物理フォーマットまで理ӕできると、ディスク操作は一挙に
厚みを増し、味わい深く、そしてExcitingな世界に様変わりします。是೪、この機会にフロッピーディ
スクの実体に迫ってみてください。
図1はフロッピーディスクの中身ですが、読み書きの上で最も重要なパーツはヘッド(Head)およびス
テッピングモーターのふたつです。ヘッドはご存じの通り、磁性体の極性を読みとるためのものであり、
フロッピーディスクを挿入し、ヘッドがロードされるとピッタリと磁性体に接するように০‫ڐ‬されていま
す。
図1フロッピーディスクの内൉構造
http://www.howstuffworks.com/floppy-disk-drive2.htm
図2はヘッド൉分の拡大図ですが、ヘッドのロード・アンロード(Load/Unload)を実現するためのスプリ
ング、およびヘッドの横方向への位置決めを決定するギアが見えています(ギーギーというシーク音の正
体)。ステッピングモーターによる横方向への移動度が、後で述べるシリンダー番号を決定します。また画
面では見えませんが、このヘッドの対側にはもうひとつのヘッドが位置しており、フロッピーディスクの
磁性体を2つのヘッドが裏表から挟み込むようになっています。
-2-
図2ヘッド拡大図
ヘッド移動のためのギア、ヘッドロード・アンロードのためのスプリングが見える
http://www.howstuffworks.com/floppy-disk-drive2.htm
さて問題のディスクですが、昔は8インチフロッピーディスクというお化けのように大きなものが一般
的だったのですが、現代では3.5インチしか見かけることはなくなりました。皆さんご存じの通り、こ
の3.5インチ・フロッピーの中には‫ݪ‬茶色の磁性体円盤、一枚が入っています。その姿は、‫ؿ‬色の
シャッターを指先でこじ開けることで、ସ方形の窓からチェックすることができます。この横ସの窓൉分
に先ほどのヘッドがロードすることで読み書きが実現されます。磁性体は、私達が見ると均一で真っ‫ݪ‬な
円盤にしか過ぎませんが、ヘッドの目からは次のように見えています(図3)。
-3-
図33.5インチ・フロッピーディスクにおけるトラック・セクター構造
中心൉の左横に位置している縦ସの穴が後述のインデックスホール
http://www.howstuffworks.com/floppy-disk-drive2.htm
同心円上に位置している円周ひとつひとつが「トラック(Track)」と呼ばれるものです。この図では合
‫ڐ‬11トラックしか書き込まれていませんが、実際には80トラックが所狭しと並んでいます。
次に、トラックを放射状に分断したものが「セクター(Sector)」です(Ҏ色൉分)。この図では1トラッ
クあたり8セクターから構成されていますが、標準的な1440KBフォーマットでは、1トラック18
セクターという決まりになっています。つまり、ひとつの「面」には合‫ڐ‬1440セクターが存在する‫ڐ‬
算になります(18x80=1440)。
フロッピーディスクが開発された当初、磁性体円盤は片面だったのですが、その後技術は進歩し両面を
読み書きすることが可能になっています。このため、ヘッドは表裏に2個が搭載されています。以上よ
り、一枚のフロッピーディスクには両面併せて2880セクターが記཈されることになります。
セクターに格納されるデータ領域のସさは、トラックあたりのセクター数によって変化しますが(後
述)、18セクター/トラックの場合512バイトです(逆に8セクター/トラックの場合は1024バイ
トに০定可能)。よって、総バイト数は512x2880=1474560となります。
ここで、参考までにハードディスクの内൉構造についても触れておきましょう。分かりやすく表現すれ
ば、ハードディスクとはフロッピーディスクが縦に連なったものです。図4は、ハードディスクの構造を
模式化したものですが、ここでは合‫ڐ‬4枚の磁性体(ハードディスクではplatterと呼ばれる)が描かれて
います。このプラッター一枚一枚は、先ほどのフロッピーディスクに内蔵されていた円盤と基本的には同
じものです。
ですから、ハードディスクにおいても読み書きの位置決めは、ヘッド・トラック・セクターの3要素で
-4-
決定されます。なお、図中でシリンダー(Cylinder)という新しいۗ葉が登場していますが、これは同じ円
周上に位置する上下のトラックを一括して扱うための概念です。フロッピーディスクにおいても表裏に2
本のトラックが存在するため、シリンダーは存在します。実際、フロッピーディスクコントローラーを制
御する場合には、シリンダー・ヘッド・セクター番号を用いますので、図3および4のイメージはしっか
りと頭に焼き付けておきましょう。
図4ハードディスクにおけるシリンダーおよびトラックの関係
http://geminga.it.nuigalway.ie/staff/h_melvin/imi/lect5.pdf
■物理フォーマットの構造
ディスクの基本構造が理ӕできたところで、「秘境」に踏み込みましょう。ここからが面白いところで
す。ユーザーがデータをやり取りする場合、read/writeシステムコールはファイルシステムが‫ڐ‬算したセ
クターに512バイト単位でデータを記཈しますが、このセクター位置は、ヘッド番号、シリンダー番
号、セクター番号の「3つ組(triad)」によって、決定されます。
-5-
ここで、皆さんにࡐ問です。「それでは、ディスクは3つ組によって指定されたセクター位置をどう
やって割り出すのでしょうか?」ヘッドは஢気的に選択することでOKですね。トラックは先ほど説明し
たステッピングモーターにより、正確に円周の位置決めを行ってあげれば良さそうです。それではセク
ターは?うん、グルグル回ってにゃんこの目?お、最初が分からんぞ?Áどないしょ?
そう、セクターは「円」の中に存在しますから、先頭を知るための特別な方策が必要となりますが、こ
のために開発された仕組みが「インデックスホール」です。図5は8インチフロッピーのインデックス
ホールを図示したものですが、ディスクドライブは回転の度にこの穴を検出することでトラックの先頭が
現れたことを知ります。具体的には、インデックスホール検出後に初めて現れたセクターが、そのトラッ
クにおける先頭セクターであることになります。後は、この先頭セクターから順番にセクター18までが
連続して連なっている・・と考えるのが普通ですが、実はそうではないのです。
図5インデックスホールと先頭セクターの関係
中心൉右斜め下の小さい穴がインデックスホール
http://www.accurite.com/FloppyPrimer.html
この問題について考える前に、物理フォーマットについてさらに踏み込んでおきましょう。先程来、位
置決めはヘッド・シリンダー・セクター番号の3つ組で決定すると説明してきました。しかしۗ葉でۗう
のは簡単ですが、実際には೪常に微細なコントールを要する作業です。ディスクの回転数や、ステッピン
グモーターにはある程度のブレがありますので、これらに対応できるように、ディスクの物理フォーマッ
トにはあらかじめ「ギャップ(Gap)」と呼ばれる余裕が০けられています。このギャップにはトラック間
およびセクター間の2種་があります(図6)。
通常、トラック間のギャップは物理的に固定されていますが、セクター間のギャップସはユーザーが自
由に০定することが可能です。あら、あなた今目がキラリと光りましたね?そうです、ギャップସを「ケ
チる」ことでデータ領域を「水増し」することができるのですÁマニアの間では特別なフォーマットを施
すことで、1.44MBのディスクを1.68MB/1.72MBとして利用するノウハウが知られていますが、この技術
背景には「ギャップସの短縮化」があった訳です。当然のことながら、ギャップを短縮すればそれだけ読
み書きに微妙なタイミングが必要とされますから、精度の悪いモーターを採用しているドライブでは読み
書きに失敗する可能性が‫ݗ‬まります。
-6-
図6ギャップ構造
http://geminga.it.nuigalway.ie/staff/h_melvin/imi/lect5.pdf
それでは話をセクターに戻し、その内൉構造を検討してみましょう。図7はセクター内൉構造の概略で
すが、データフィールドの他にもいくつかの構造が存在しています。EC(ErrorCorrection)はデータの
正当性をチェックするためのCRCコードですが、注目すべきはAddressMark(AM)です。これは名前の
通り、セクターのアドレスを保持するためのフィールドです。љい方は、「インデックスホールで先頭セ
クター1番さえ検出できれば、わざわざアドレスをセクター内൉に埋め込む必要はないのではないか?」
と考えておられることでしょう。おっしゃる通りなのですが、その理由は次回明らかになります。図中で
グレーの൉分はBufferZoneと名付けられていますが、これが上で説明したギャップ構造になります。
せっかくの機会ですから、その詳細を見てみましょう。
-7-
図7セクターの内൉構造・概略
Referencemarkがインデックスホールに相当。
http://www.upscale.utoronto.ca/GeneralInterest/Drummond/Micro/ln_disk.pdf
図8は初期のWinchester型ハードディスクにおける、セクター構造を示したものです(※※※フロッ
ピーディスクのセクター構造で差し替え予定※※※)。この中にGap1/Gap2/Gap3の存在が見て取れま
す。Idと名付けられた൉分が先ほどのAddressMarkに相当しますが、この中にトラック・ヘッド・セク
ター番号が格納されている点に注目してください。
図4セクターの微細フォーマット構造(ST506フォーマットの場合)
http://geminga.it.nuigalway.ie/staff/h_melvin/imi/lect5.pdf
フロッピーディスクの場合、このID൉分は以下の4項目から構成されています(それぞれの頭文字を並
-8-
べ「CHRN」と略称)。
・シリンダー番号(Cylinderaddress)
・ヘッド番号(Headaddress)
・セクター番号(sectoRaddress)
・セクターସ(Numberofbytesinasector)128*2^N下記表参照
CHRNにはアドレス番号だけでなく、セクターସまで含まれていますが、これは一体何を意味してい
るのでしょうか?次回明らかになりますが、実はフロッピーディスクではセクターସをトラック毎、さら
にはセクター毎に変化させることが可能なのです。例えば、トラック1の先頭セクターは512バイト、
第二セクターは1024バイト、第三セクターは再び512バイト・・という具合です(Hybridsector
formatとでも呼ぶべきでしょうか?)。
もちろん、このような特殊フォーマットは通常のコマンドでは作成できませんし、読み込むこともでき
ません。その昔、ゲームソフトがフロッピーディスクで配布されていた時代は、Hybridsectorformatな
どでディスケットにコピープロテクションが施されていたものです。コピーツールがすっかり廃れてし
まった現在では、逆に物理フォーマットを活用したコピープロテクションが案外効果的かもしれません
ね。
■CHRNを見る
ここまでのӕ説で、フロッピーディスク・フォーマットの基本知ࡀは十分に把握できたことと思います
が、これらはあくまでも机上の空論に過ぎません。生きた知ࡀとして身につけるためには、実際に自分の
手を動かし、自分の目で確認する作業が不可欠です。後半では、Linux上のfdrawcmdツールを用いなが
ら、ディスクの内൉構造を検証してみましょう。
まず、利用しているディストリビューション中に最新のfdutilsパッケージが用意されているかどうか
確認してください(2002/12/27時点で最新版は5.4)。もしも古いようでしたら、今回最新版fdrawcmdの
ソースを用意しましたので、こちらをお使いください。
-rw-r--r--1nobodysrc11666Nov602:27fdrawcmd.1
-rw-r--r--1nobodysrc8538Apr22000fdrawcmd.c
gcc-Wall-O2-ofdrawcmdfdrawcmd.cでビルド出来ます。manファイルも打ち出しておきましょう
(参照はman-lfdrawcmd.1、PSプリンターへの印刷はman-t-lfdrawcmd.1|lp)。fdrawcmdの使い
-9-
方は以下の通りです。
fdrawcmd[オプション]実行コマンド
オプション項目
repeat=繰りඉし回数
指定された回数、コマンドを繰りඉして実行
(但しread/writeなどのデータ転送を伴うコマンドは不可)
length=転送バイト数
データ転送を必要とするコマンドの場合は、そのバイト数を指定
データをFDCへ送る場合は標準入力から読み込まれ、
受け取る場合は標準出力へ書き込まれる
デフォルトでは、EOFが検出されるまで転送が続く
short
結果を一行で表示する
print_time
コマンド実行直後のタイムスタンプをμ秒単位で表示する
do_buffer
繰りඉしの間、タイムスタンプをワークエリアに記཈しておき
fdrawcmd終了時にまとめてコマンド実行結果およびその
タイムスタンプを表示する(printf実行によるタイムラグをේぐ)
実行コマンド
recalibratedrvsel
ドライブの再キャリブレーションを実行
(ヘッドはシリンダー0番へ移動)
seekdrvselc
指定したシリンダーアドレスへヘッドを移動
readiddrvsel
「最初に」ヘッドが出会ったセクターIDを読み込む
readdrvselchrnsptgapszcod2
指定されたCHRNアドレスを持ったセクターの内容を読み込む
lengthオプションで指定した転送バイト数分、標準出力へ
出力される(リダイレクションを忘れないように)
writedrvselchrnsptgapszcod2
指定されたCHRNアドレスを持ったセクターに標準入力の
内容をlengthオプションで指定した転送バイト数分、書き込む
(リダイレクションを忘れないように)
formatdrvselnsptgapfill
トラックフォーマットの実行
セクター数分のCHRNテーブルを標準入力を通じて転送する
drvsel
ビット0および1でドライブ番号
ビット2でヘッド番号を指定(通常0もしくは4)
spt
SectorPerTrackトラック当たりのセクター数(18)
gap
Gap2(アドレス・データフィールド間ギャップ)の
バイト数(通常27を指定)
- 10 -
szcod2
通常255を指定
fill
フォーマット時にデータ領域を初期化するコード
何やらややこしそうですが、習うより慣れろです。早速、実験してみましょう。まず最初に、フロッ
ピードライバーおよびFDC(FloppyDiskController)の初期化コマンドをԑえておきましょう
(recalibrateコマンドによりヘッドはシリンダー0番に移動します)。
───────────────────────────────────────────
#floppycontrol--resetnow2
#fdrawcmdrecalibrate0
───────────────────────────────────────────
途中で"rawcmd:Input/outputerror"が表示された場合は、上の2つのコマンドを入力し、再度初期
化を行ってください。それでは、手始めにreadidコマンドに挑戦してみましょう(以下、ルート権限で実
行してください)。
───────────────────────────────────────────
#fdrawcmdreadid0
0:0
1:0
2:0
3:0
4:0
5:4
6:2
diskchange
#fdrawcmdreadid0
0:0
1:0
2:0
3:0
4:0
5:2
6:2
diskchange
───────────────────────────────────────────
連続して2回readidコマンドを実行しています。通常FDCの制御コマンドを実行した際には、この
ように7バイトのデータがඉされます。先頭の3バイトはステータスコード(ST0/ST1/ST2)、後ろの4バイ
トはCHRNアドレスです。この場合、1回目のreadidではCHRNとして0042、2回目には
0022がඉされていることが分かります。N=2は先ほどの表から512バイトセクターであることを
意味していますから、1回目はシリンダーアドレス0番、ヘッドアドレス0番、セクターアドレス4番、
2回目は同じく0、0、2番のセクター(512バイト)IDを読み取ったことになります。前もって、
recalibrateコマンドを実行していたため、シリンダー番号がいずれも0になっていること、およびセク
ター番号はコマンド実行時にたまたまヘッドの後方に位置していたセクターIDに由来している点に注意
- 11 -
してください。
なるほど、こうして見ると確かに「円盤は回っている」ことが実感できますね。それでは、「一気読
み」するとどうなるでしょうか?これにはrepeatオプションを活用します。
───────────────────────────────────────────
#fdrawcmdshortrepeat=20readid0
0x000x000x000x000x000x120x02disk_change
0x000x000x000x000x000x010x02disk_change
0x000x000x000x000x000x020x02disk_change
0x000x000x000x000x000x030x02disk_change
0x000x000x000x000x000x040x02disk_change
0x000x000x000x000x000x050x02disk_change
0x000x000x000x000x000x060x02disk_change
0x000x000x000x000x000x070x02disk_change
0x000x000x000x000x000x080x02disk_change
0x000x000x000x000x000x090x02disk_change
0x000x000x000x000x000x0a0x02disk_change
0x000x000x000x000x000x0b0x02disk_change
0x000x000x000x000x000x0c0x02disk_change
0x000x000x000x000x000x0d0x02disk_change
0x000x000x000x000x000x0e0x02disk_change
0x000x000x000x000x000x0f0x02disk_change
0x000x000x000x000x000x100x02disk_change
0x000x000x000x000x000x110x02disk_change
0x000x000x000x000x000x120x02disk_change
0x000x000x000x000x000x010x02disk_change
───────────────────────────────────────────
結果をコンパクトに表示するためにshortオプションを使っていますが、トラックの回転を可視化する
ことができました。CHRNが最終セクター18番から始まったかと思うと、突然先頭セクター1番が現
れ、その後順番に一巡した後、再び先頭セクターへඉっていることが分かります。面白いですね。ついで
に、「裏面」も覗いてみましょう。
───────────────────────────────────────────
#fdrawcmdshortrepeat=20readid4
0x040x000x000x000x010x0e0x02disk_change
0x040x000x000x000x010x0f0x02disk_change
0x040x000x000x000x010x100x02disk_change
0x040x000x000x000x010x110x02disk_change
0x040x000x000x000x010x120x02disk_change
0x040x000x000x000x010x010x02disk_change
0x040x000x000x000x010x020x02disk_change
0x040x000x000x000x010x030x02disk_change
0x040x000x000x000x010x040x02disk_change
0x040x000x000x000x010x050x02disk_change
- 12 -
0x040x000x000x000x010x060x02disk_change
0x040x000x000x000x010x070x02disk_change
0x040x000x000x000x010x080x02disk_change
0x040x000x000x000x010x090x02disk_change
0x040x000x000x000x010x0a0x02disk_change
0x040x000x000x000x010x0b0x02disk_change
0x040x000x000x000x010x0c0x02disk_change
0x040x000x000x000x010x0d0x02disk_change
0x040x000x000x000x010x0e0x02disk_change
0x040x000x000x000x010x0f0x02disk_change
───────────────────────────────────────────
今度はdrvselに4を指定します(ヘッド番号はビット2)。確かにHの欄が全て01に変化しています
ね。セクターの回転も先ほどと同様です。
■回転数を見る
CHRNの正体を自分の目で確かめたところで、今度はモーターの「回転数」を可視化してみましょ
う。このためには、先ほど紹介したprint_time,do_bufferオプションの2つを用います。print_time
により、正確なFDCコマンド実行時間が分かりますが、このままですと途中でセクターが1つ飛んでしま
うことがあります。printf関数実行によりૺ延が発生し、readidコマンドの発行がૺれてしまうためで
す。このため、repeatオプションでコマンドを連続実行する場合は、忘れずにdo_bufferオプションを
指定するようにしてください。本オプションを指定しておけば、セクターの取りこぼしはなくなります。
それでは、実行です。
───────────────────────────────────────────
#fdrawcmddo_buffer print_timeshortrepeat=32readid0
0x000x000x000x000x000x0a0x02disk_change462493
0x000x000x000x000x000x0b0x02disk_change473395
0x000x000x000x000x000x0c0x02disk_change484302
0x000x000x000x000x000x0d0x02disk_change495211
0x000x000x000x000x000x0e0x02disk_change506126
0x000x000x000x000x000x0f0x02disk_change517037
0x000x000x000x000x000x100x02disk_change527941
0x000x000x000x000x000x110x02disk_change538850
0x000x000x000x000x000x120x02disk_change549774
0x000x000x000x000x000x010x02disk_change564333
0x000x000x000x000x000x020x02disk_change575251
0x000x000x000x000x000x030x02disk_change586155
0x000x000x000x000x000x040x02disk_change597058
0x000x000x000x000x000x050x02disk_change607972
0x000x000x000x000x000x060x02disk_change618888
0x000x000x000x000x000x070x02disk_change629799
0x000x000x000x000x000x080x02disk_change640703
- 13 -
0x000x000x000x000x000x090x02disk_change651609
0x000x000x000x000x000x0a0x02disk_change662522
0x000x000x000x000x000x0b0x02disk_change673434
0x000x000x000x000x000x0c0x02disk_change684336
0x000x000x000x000x000x0d0x02disk_change695249
0x000x000x000x000x000x0e0x02disk_change706164
0x000x000x000x000x000x0f0x02disk_change717077
0x000x000x000x000x000x100x02disk_change727977
0x000x000x000x000x000x110x02disk_change738886
0x000x000x000x000x000x120x02disk_change749808
0x000x000x000x000x000x010x02disk_change764374
0x000x000x000x000x000x020x02disk_change775290
0x000x000x000x000x000x030x02disk_change786205
0x000x000x000x000x000x040x02disk_change797103
0x000x000x000x000x000x050x02disk_change808016
───────────────────────────────────────────
CHRN0-0-1-2というセクターIDに注目してください。最初に0-0-1-2が出現したのは564333μ
sec、2回目に出現したのが764374μsecですから、その間隔は200041μsec(約200msec)ということ
になります。これを分当たりに‫ڐ‬算すると、60/0.2=300rpm(RoundPerMinute)。すなわち、フロッ
ピーディスクのモーターは1分間あたり300回転していることが明らかになりました。300rpmという
のは大切な数字ですから、よくԑえておいてください。それにしても、フロッピーディスクは最‫ؼ‬のハー
ドディスクに比べると1/20‫ؼ‬いスロースピードだったのですね。
ここで、セクター間の実時間を‫ڐ‬算してみましょう。セクター番号(CHRN)0-0-1-2/0-0-2-2/0-0-3-2/00-4-2/0-0-5-2の間隔はそれぞれ10918,10904,10903,10914μsecになりますので、セクター間で
10
μsecの変動が存在することが分かります。このブレを緩衝(buffer)するために、前半で紹介した「セク
ター間ギャップ」が導入されたのです(詳細は次回)。
■FDC制御によるセクター入出力
次は、いよいよFDC制御による「セクター読み込み」に挑戦してみましょう。ࠟしに、FreeDOSのブー
トセクターを読みこんでみます(フロッピーディスクのブートセクターアドレスはシリンダー0、ヘッド
0、セクター1)。
───────────────────────────────────────────
#fdrawcmdlength=512read000121827255> boot
remaining= 0
0:0
1:0
2:0
3:0
4:0
5:2
6:2
- 14 -
diskchange
#wc-cboot
512boot
───────────────────────────────────────────
CHRNのN値を間違えやすいので注意してください。512バイトセクターの場合は、2です。また
最後のふたつのパラメーターは、27および255を指定してください。lengthオプションで転送バイト
数を512バイトとし、リダイレクションでbootファイルにセクターデータを記཈しています。リザル
トコードにおけるCHRNは次に読み込むセクターのアドレス(0-0-2-2)を示しています。ファイルの大き
さは、ぴったり512バイトですね。中身を確認してみましょう。
───────────────────────────────────────────
#objdump-bbinary-sboot
boot:fileformatbinary
Contentsofsection.data:
0000eb4c9046726565444f53200002010100.L.FreeDOS.....
001002e000400bf009001200020000000000...@............
002000000000000029d7115534425241494e......)..U4BRAIN
003053544f524d2046415431322020206000STORMFAT12`.
00400e00010000001300000021000000fafc..........!.....
005031c08ed08ed8bd007c8d66e0fbcd13b81.......|.f.....
0060e01f8ec089ee89efb90001f3a5ea727c..............r|
0070e01f8ed88ed0885624e8e5004c6f6164.......V$...Load
0080696e672046726565444f5320008b4646ingFreeDOS..FF
[email protected]>`..F>
00a0e8c600723831ffb90b00beb97d57f3a6...r81......}W..
00b05f268b451a740b83c72026803d0075e7_&.E.t...&.=.u.
00c0726f50e89b0020464154008e463e31dbroP...FAT..F>1.
00d08b7e168b46428b5644e88d005872521e.~..FB.VD...XrR.
00e0061f07bf0020ab89c601f601c6d1eead...............
00f07304b104d3e880e40f3df80f72e831c0s........=..r.1.
0100ab0e1fe85b00204b45524e454c008e46....[.KERNEL..F
01103e31dbbe0020ad09c0742f48488b7e0d>1......t/HH.~.
012081e7ff00f7e703464a13564ce83a0073.......FJ.VL.:.s
0130e5e82d000d0a424f4f54206572726f72..-...BOOTerror
0140210d0a0030e4cd16cd19e8140020474f!...0........GO
01502120008a5e24ea0000600031dbb40ecd!..^$...`.1....
0160105eac563c0075f3c3565250918a4618.^.V<.u..VRP..F.
0170f6661a91f7f192f6761889d188c686e9.f......v.......
0180d0c9d0c98a461828e0fec408e1b80102.....F.(........
01908a5624cd13730830e4cd13585aebcb03.V$..s.0...XZ...
- 15 -
01a05e0b73078cc080c4108ec0585a050100^.s........XZ...
01b083d2004f75b4f85ec34b45524e454c20...Ou..^.KERNEL
01c020535953000000000000000000000000SYS............
01d000000000000000000000000000000000................
01e000000000000000000000000000000000................
01f0000000000000000000000000000055aa..............U.
───────────────────────────────────────────
やりましたÁFreeDOSの文字とbootsignature(最終尾の55aa)が見て取れます。UNIX上で同じこ
とはddif=/dev/fd0of=bootbs=512count=1でも実現できますが、fdrawcmdはカーネルのファイルシ
ステムを介することなく、FDCを直接制御することでセクター内容を読み込んでいます。ここまで来れ
ば、UNIXファイルシステムの正体は、論理ファイルのデータ領域をCHRNアドレスへ変換する写像関数
であることが、お分かり頂けたことと思います。
読み込みが出来たのなら、書き込みにも挑戦です。実験の前に次のプログラムを実行して、testという
ファイルを用意しましょう。
───────────────────────────────────────────
#include<stdio.h>
intmain(){
FILE*fp;
inti;
fp=fopen("test","w");
if(fp==NULL)
return1;
for(i=0;i<32;i++)
fprintf(fp,"Canyouseeme?");
fclose(fp);
}
───────────────────────────────────────────
出来上がったtestファイルを最終セクター(Head1/Track79/Sector18)に書き込みますが、その前に
seekコマンドでヘッドをシリンダーアドレス79番へ移動させます。このseek作業を忘れるとwrite
コマンドは目的のCHRNを持ったセクターを見つけることが出来ずに、タイムアウトエラーで異常終了
しますので注意してください。
───────────────────────────────────────────
#fdrawcmd seek 0 79
0:20
1:4f
nodiskchange
#fdrawcmdlength=512write 4 79 1 18 21827255< test
remaining= 0
- 16 -
0:4
1:0
2:0
3:50
4:0
5:1
6:2
nodiskchange
───────────────────────────────────────────
次にwriteコマンドでtestファイルの内容をHead1/Track79/Sector18に転送します。ヘッド番
号は1ですから、writeの最初の引数は4になります。書き込みに成功すれば、remaining=0と表示さ
れ、全データが無事書き込まれたことが分かります。本当にFDC直接制御で書き込むことができたので
しょうか?ddコマンドで確認してみましょう。
───────────────────────────────────────────
#dd if=/dev/fd0 of=fd
2880+0recordsin
2880+0recordsout
#objdump-bbinary-sfd|tail-n35
167dd0f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6................
167de0f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6................
167df0f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6................
167e0043616e20796f7520736565206d65203fCanyouseeme?
167e1043616e20796f7520736565206d65203fCanyouseeme?
167e2043616e20796f7520736565206d65203fCanyouseeme?
167e3043616e20796f7520736565206d65203fCanyouseeme?
167e4043616e20796f7520736565206d65203fCanyouseeme?
167e5043616e20796f7520736565206d65203fCanyouseeme?
167e6043616e20796f7520736565206d65203fCanyouseeme?
167e7043616e20796f7520736565206d65203fCanyouseeme?
167e8043616e20796f7520736565206d65203fCanyouseeme?
167e9043616e20796f7520736565206d65203fCanyouseeme?
167ea043616e20796f7520736565206d65203fCanyouseeme?
167eb043616e20796f7520736565206d65203fCanyouseeme?
167ec043616e20796f7520736565206d65203fCanyouseeme?
167ed043616e20796f7520736565206d65203fCanyouseeme?
167ee043616e20796f7520736565206d65203fCanyouseeme?
167ef043616e20796f7520736565206d65203fCanyouseeme?
167f0043616e20796f7520736565206d65203fCanyouseeme?
167f1043616e20796f7520736565206d65203fCanyouseeme?
167f2043616e20796f7520736565206d65203fCanyouseeme?
167f3043616e20796f7520736565206d65203fCanyouseeme?
167f4043616e20796f7520736565206d65203fCanyouseeme?
- 17 -
167f5043616e20796f7520736565206d65203fCanyouseeme?
167f6043616e20796f7520736565206d65203fCanyouseeme?
167f7043616e20796f7520736565206d65203fCanyouseeme?
167f8043616e20796f7520736565206d65203fCanyouseeme?
167f9043616e20796f7520736565206d65203fCanyouseeme?
167fa043616e20796f7520736565206d65203fCanyouseeme?
167fb043616e20796f7520736565206d65203fCanyouseeme?
167fc043616e20796f7520736565206d65203fCanyouseeme?
167fd043616e20796f7520736565206d65203fCanyouseeme?
167fe043616e20796f7520736565206d65203fCanyouseeme?
167ff043616e20796f7520736565206d65203fCanyouseeme?
───────────────────────────────────────────
来てます、来てます。最終セクターにきちんとtestファイルのイメージが上書きされていました(セク
ター17番との境界൉分に注目)。しかし、この程度で満ੰしていてはいけません。この先には、さらに刺
激的な世界が私達を待ち受けているのです。
■次回は
今回は、フロッピーディスクの基本フォーマット構造をӕ説しました。UNIX上では、デバイスはファイ
ルとして透過的に扱えるために、プログラマーがディスクの物理構造を意ࡀする必要は全くありません。
しかしこれでは、いつまで立っても自力でファイルシステムを作り上げることは出来ませんので、Linux
上のfdrawcmdを用いてFDC直接制御によるディスク読み書きを行ってみました。ファイルシステムに頼
ることなく垣間見た、ディスクの素顔はいかがだったでしょうか?普段何気なく扱っていた無機ࡐのフ
ロッピーに表情が宿り、そっと開けたシャッターの向こうにCHRNが透視できるようになれば、あなた
も立派なシステムプログラマーの仲間入りです。
次回は、今回の知ࡀをさらに発展させ、手作業によるトラックフォーマットに挑戦します。「フォー
マット」の正体が明らかになると共に、CHRNの深遠なる世界が開けることでしょう。乞うご期待Á
- 18 -