IDE ドライバはジオメトリに関する 5 つの情報源を持っています。 一つ目 (G_user) はコマンドラインでユーザによって指定されたもの。 二つ目 (G_bios) は BIOS の Fixed Disk Parameter Table (一台目と二台目のディスクに限る) で、 32 ビットモードに移行する前のシステム起動時に読み込まれます。 三つ目 (G_phys) と四つ目 (G_log) は IDENTIFY DEVICE コマンドに対して IDE コントローラが答える情報で、それぞれ「物理」「現状の論理」ジオメトリです。
一方、ドライバはジオメトリとして二つの値を必要とします。
一つ (G_fdisk) は HDIO_GETGEO
ioctl が返す値、
もう一つ (G_used) は I/O を行う際に実際に使用される値です。
G_user が与えられていれば、 G_fdisk と G_used はそれで初期化されます。
G_user が与えられず、CMOS から情報 (G_bios) が取得できれば、
それに設定されます。それ以外のときには、G_phys に設定されます。
G_log が妥当であるようなら、G_used はそれに設定されます。
あるいは、G_used の値が妥当ではなく、G_phys が妥当であるようなら、
G_used には G_phys の値が設定されます。
ここでの「妥当」とは、ヘッド数が 1-16 の範囲に入っていることです。
言い換えると、コマンドラインでの指定は、BIOS からの値を上書きして
fdisk
がどのようにディスクをとらえるかを決めます。
しかし、もし、(ヘッドが 16 を超える)変換後のジオメトリを指定すると
カーネル I/O ではその値は破棄され、IDENTIFY DEVICE
コマンドからの値が使用されます。
G_bios はそんなに信用できないことに注意してください: SCSI からシステムを起動するために一台目と二台目のディスクが SCSI ディスクだったとしても、 sda のために BIOS が報告するジオメトリはカーネルでは hda のために使われます。 そのうえ、BIOS セットアップで認識させていないディスクは BIOS からは見えません。これは次のようなことを意味します。 例えば、BIOS セットアップで hdb が認識されていない IDE オンリーのシステムでは、一台目と二台目のディスクについて BIOS が報告するジオメトリは hda と hdc のものでしょう。 (訳注: つまりディスク hdb に対して、 hdc のジオメトリが適用されるということです。)
SCSI での事態は少し違っていて、
SCSI コマンドは既に論理ブロック番号を使っているので、
ジオメトリは実際の I/O に関してはまったく不適切なものです。
しかし、パーティションテーブルのフォーマットは未だに IDE と同じなので、
fdisk はいくつかのジオメトリをでっち上げなければならず、
また HDIO_GETGEO
も使わなければなりません - 確かに、fdisk
は
IDE ディスクであろうが SCSI ディスクであろうが区別はしません。
以下の詳細な記述を読めば、種々のドライバがそれぞれ、
ちょっとずつ異なったジオメトリをでっち上げるのが分かります。
本当に困ったもんだ。
DOS やその類を使わないのならば、すべての拡張変換設定を避けて、 64 ヘッド 32 セクタ毎トラックを使ってください (これだと 1 MiB/シリンダとなりとても便利です)。 またこれならば、ディスクをあるコントローラから他のコントローラに 繋ぎ換えても問題は起きないでしょう。 いくつかの SCSI ディスクドライバ(aha152x, pas16, ppa, qlogicfas, qlogicisp)は DOS との互換性にとても神経質で、 Linux のみのシステムでも 8 GiB を越えるディスクの使用を許してくれないでしょう。 これはバグです。
本当のジオメトリはどうなっているのでしょう?
それは簡単、そんなものはありません。
多分知りたくもないでしょうが、まぁ、そんなものがあったとしても
絶対に 決して 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
のほとんどの版で、取り扱える容量を 8 GiB に限定してしまいます。
これは、今日の実際の上限となっています。
以下の記述では、M をディスクの総容量、C,H,S をシリンダ、ヘッド、 トラックあたりのセクタ数とします。 C を M / (H*S) で定義されると考えるなら、H,S を得るだけで十分です。
H=64, S=32 を初期値とします。
H=64, S=32 です。
C > 1024 になるまでは、H=64, S=32。
それ以降は、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 の `> 1 GB' オプションが入っていれば 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です。 Linux 2.0.36 では、 SEEPROM が見付からない場合はこの拡張変換を常に有効にしますが、 Linux 2.2.6 では、SEEPROM が見付からない場合は拡張変換は、 ユーザがをれを使用するように起動パラメータを指定した時のみ有効になります (SEEPROM が見付かった場合は起動パラメータは無視されます)。 これは、2.0.36 で設定したものが 2.2.6 では起動に失敗するかもしれない (そして、LILO に `linear' キーワードが必要になったり、 カーネルの起動パラメータ `aic7xxx=extended' が必要になったりするかもしれない) ことを意味しています。
C >= 1024 になるまでは H=64, S=32。 それ以降はコントローラの拡張変換が使用されている場合、 M < 2^22 (訳注: 2 GiB) ならば H=128, S=32 で、 さもなければ 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) として使用します。パーティションがないならば次のようになります: M < 2^21 (1 GiB) ならば H=64, S=32 、 M < 63*2^17 (3.9 GiB) ならば H=128, S=63 、 さもなければ H=255, S=63 。
(H,S) = (64,32), (64,63), (128,63), (255,63)のうち、 C <= 1024 となる最初のものを使用します。 最後のものを使うときには、C は 1023 に丸められます。
C,H,S をディスクから読みます(ひぃぃぃぃっ!)。 C か S が大きすぎれば、 S=17, H=2 として、 C <= 1024 となるまで H を倍にしていきます。 これは M > 128*1024*17 (1.1 GiB) のとき、 H が 0 に設定されることを意味します。これはバグです。
コントローラのマッピングモードに応じて (H,S) = (16,63), (64,32), (64,63) のうちの一つが使用されます。
パーティションテーブルを見てみましょう。
パーティションの終わりをシリンダ境界に置くという規則から、
どのようなパーティションであってもパーティションの終わりが
end = (endC,endH,endS)
であるとき、
単に H = endH+1
でS = endS
と計算することができます
(繰り返しますが、セクタは 1 から数えます)。
もう少し厳密にいうと、以下のようになります。
もしパーティションが存在するならば、
beginC
がもっとも大きいパーティションを選びます。
そうして、end+1
を、start
と length
を足す方法と、
パーティションの終わりがシリンダ境界にあると仮定する方法で計算します。
もし両方の答えが一致するか、endC
= 1023 かつ
start+length
が (endH+1)*endS
の整数倍ならば、
このパーティションは本当にシリンダ境界にそろえられていると考えられます。
もし、これがだめなようならば、つまりパーティションが全然無いか、
大きさがなんか変ならば、ディスク容量 M のみを見ます。
アルゴリズムは以下のようになり、
この方法は、C を最大 1024, S を最大 62 に押さえるような効果があります:
H = M/(62*1024) で切り上げ、
S = M/(1024*H) で切り上げ
C = M/(H*S) で切り捨て。