結局どうだというのでしょう? Linux 利用者にとっては一点だけが 重要です。それは、LILO と fdisk が正しいジオメトリを使わなければ ならない点です。ここで「正しい」とは fdisk に関しては同じディスク 上の他の OS がそのジオメトリを使用しているという意味であり、また、 特に LILO に関してはうまく BIOS とやり取りして起動できる ジオメトリであるということです。(普通は同じジオメトリが両者にとって 正しいジオメトリになります)
Fdiskは、どうやってジオメトリを調べるのでしょう? HDIO_GETGE
ioctl を使用してカーネルに聞くのです。しかし、
利用者は対話的に、あるいはコマンドラインからジオメトリを強制できます。
LILO は、どうやってジオメトリを調べるのでしょう?HDIO_GETGE
ioctlを使用してカーネルに聞くのです。
しかし、利用者は "disk=
" オプションを
使って、ジオメトリを強制できます。Linear オプションをLILOに指定する
こともできます。その場合、LILO CHSアドレスの変わりに LBA アドレス
をマップファイルに格納し、あとで起動するときにそれを使用します。
(INT 13のファンクション8を問い合わせに使用します)
カーネルは答えをどうやって調べるのでしょうか?ええと、最初に
利用者 "hd=
cyls,
heads,
secs"
コマンドラインオプションによって明示的
にこれを指定するかもしれません。でなければ、カーネルはハードウェアに
問い合わせます。
ちょっと考えてみましょう。IDE ドライバーはジオメトリに関する 情報源を4つ持っています。一つ目はコマンドラインからの利用者による 指定です(G_user)。二つ目は 32 ビットモードに遷移する前に読み込まれる BIOS Fixed Disk Parameter Table です (G_bios) (この情報は最初の 2ドライブだけに関してです)。三つ目と四つ目は IDE コントローラが IDENTIFYコマンドに対して答える情報で、それぞれ、「物理」「現状の論理」 ジオメトリです(それぞれ G_phys、G_log)。
その一方で、ドライバはジオメトリとして 2 つの値を必要とします。一つは HDIO_GETGEO ioctl が返す値です(G_fdisk)。もう 1 つは、実際に I/O を行うときに返す値です(G_used)。G_userが与えられていれば、 G_fdisk と G_used はそれに設定されます。G_userが 与えられず、 CMOS RAMの なかに 情報があれば、それに設定されます。それ以外のときには、G_physに 設定されます。G_log が妥当であるようなら、G_used はそれに 設定されます。あるいは、G_used の値が妥当ではなく、G_phys が 妥当であるようなら、G_phys の値が設定されます。ここで 「妥当」とは、ヘッドの値が 1-16 の範囲に入っていることです。
言い換えると、コマンドラインでの指定は、BIOS からの値を 上書きして fdisk がどのようにディスクをとらえるかを決めます。しかし、 もし、(ヘッドが 16 を超える)変換後のジオメトリを指定すると カーネル I/Oで はその値は破棄され、IDENTIFY コマンドからの 値が使用されます。
本当のジオメトリはどうなっているのでしょう?こいつは簡単で、 そんなものはありません。多分知りたくもないでしょうが、まぁ、 そんなものがあったとしても絶対に、fdisk や LILO やカーネルに そんなものを指定しないで下さい。ジオメトリは SCSI コントローラ とディスクの間でだけ使うべきものです。くどいようですが、 SCSI ディスクのジオメトリを fdisk や LILO やカーネルに指定する ような奴はアホです。
それでも好奇心のあまりどうしてもと言い張るのであれば、ディスク自身に 問い合わせることができます。SCSI コマンドには、総容量を調べる READ CAPACITY コマンドがあります。また、MODE SENSE コマンドの Rigid Disk Drive Geometry ページ(ページ04)はシリンダ数とヘッド数を教えてくれます(変更はできません)。 Format ページ(ページ03)はセクターあたりのバイト数とトラックあたりのセクター 数を教えてくれます。後者は普通ノッチに依存します、つまり、 トラックあたりのセクター数は可変でして、外側のトラックには 内側よりたくさんのセクターが入っています。Linux の scsiinfo プログラムは こういった情報を教えてくれます。こういった情報は詳細を極めた上に 複雑この上なく、おまけに明らかに誰も(多分 OS ですら)こんな情報は使いません。 その上、fdisk と LILO に関する限り、普通に返される C/H/S = 4476/27/171 などという値は使い物になりません。というのは、パーティションテーブルは C/H/S に 10/8/6 ビットづつしか割り当てていないからです。 (訳注:原著者に問い合わせたところ、ノッチとは「一つのディスクの中で、 トラックあたりのセクタ数が同じ領域」とのことです。)
では、カーネルは HDIO_GETGEO
のための情報をどこから持ってきているのでしょう。
これは SCSI コントローラから持ってくるか、何か賢い方法で作ってしまい
ます。ドライバーによっては我々が「真実」を欲しがっていると考えて
いるようですが、どっこい、われわれは DOS や OS/2 の FDISK (や、Adaptec
の AFDISK なんか)が使う数字がほしいだけです。
ところで、Linux の fdisk は H と S つまり、ヘッド数とトラックあたりの セクター数だけを使って LBA を C/H/S アドレスに変換します。しかし、 シリンダ数 C は使用しません。ドライバによっては、ドライバが 1023*255*63 セクタに対応できることを知らせるために、(C,H,S)=(1023,255,63) であると 報告してきます。不幸なことに、これからは実際の容量はわかりませんし、 fdisk のほとんどの版で、取り扱える容量を 8GB に限定してしまいます。 これは、今日の実際の上限となっています。
以下の記述では、M を総容量、C,H,S をシリンダ、ヘッド、トラックあたりの セクタの数とします。C を M/(H*S) で定義されると考えるなら、H,S を得る だけで十分です。
H=64, S=32 を初期値とします。
H=64, S=32.
C > 1024 になるまでは、H=64, S=32。 > 1024 になる場合は、H=255, S=63, C=min(1023,M/(H*S))。(このため、C は丸められており、H*S*C は ディスクの総容量 M とはなりません。これは、たいていの版の fdisk で問題となります)。ppa.c のコードでは、M ではなく、M+1 を使用し ており、sd.c のバグにより、 M は 1 のオフセットを持っていると 書かれています。
C > 1024 になるまでは、H=64, S=32。それ以降は BIOS の "> 1GB' オプションが入いっていれば H=255, S=63 です。
コントローラに 2 つの内のどちらの変換方法を用いているか問いあわせ、 H=255, S=63 か、H=64, S=32 の何れかを使用します。前者を使っている 場合は、起動メッセージとして "aha1542.c: Using extended bios translation" と表示されます。
C > 1024 になるまでは H=64, S=32。それ以降は 、"extended"起動パラメータ がドライバに与えられているか SEEPROM か BIOS の「拡張」ビットが立てられ ているならば、 H=255, S=63です。
C >= 1024 になるまでは H=64, S=32。それ以降はコントローラの拡張変換が 使用されている場合、M < 2^22 ならば H=123, S=32 で、M >= 2^22 なら ば、H=255, S=63 です。しかし、この(C,H,S)設定を行った後、パーティション テーブルを読み出します。そして、可能な3つの組み合わせ、(H,S) = (64,32), (128,32), (255,63) のうちから、endH=H-1 になりそうなものを さがします。そして、次のような起動時メッセージを表示します。 "Adopting Geometry from Partition Table"
BIOS ドライブパラメータテーブルのジオメトリ情報を探すか、パーティション テーブルを読んで、パーティションがあるなら最初のパーティションの(endH+1,endS) を(H,S)として使用します。パーティションがないならば
(H,S) = (64,32), (64,63), (128,63), (255,63)のうち、<=1024 となる最初の ものを使用します。最後のものを使うときには、C=1023 に丸められます。
C,H,S をディスクから読みます(ひぃぃぃぃっ!)。C か S が大きすぎれば、 S=17, H=2 として、C<=1024 となるまで H を倍にしていきます。これは M < 128*1024*17 (1.1GB) のとき、H が 0 に設定されることを意味します。 これはバグです。
コントローラのマッピングモードに応じて (H,S) = (16,63), (64,32), (64,63) のうちの一つが使用されます。
パーティションテーブルを見てみましょう。パーティションの終わりを
シリンダ境界に置くという規則から、どのようなパーティションであっても
パーティションの終わりが end = (endC,endH,endS)
であるとき、
単に H = endH+1
and S = endS
と計算することができます(繰り返しますが、
セクタは 1 から数えます)。もう少し厳密にいうと、以下のようになります。
もし、パーティションが存在するならば、beginC
がもっとも大きいパーティ
ションを選びます。そうして、end+1
を、start
と length
を足す方法と、
パーティションの終わりがシリンダ境界にあると仮定する方法で計算します。
もし両方の答えが一致するか、endC = 1023
かつ start+length
が
(endH+1)*endS
の整数倍ならば、このパーティションは本当にシリンダ境界
にそろえられていると考えられます。もし、これがだめなようならば、つまり
パーティションが全然無いか、大きさがなんか変ならば、ディスク容量
M のみを見ます。アルゴリズムは: