『オペレーティングシステム - 設計と実装 (第3版)』 を読む。第8日目。プロセス 5(ブートモニタ)
boot.cのコードを見ていく。
boot(void)
void boot(void)
/* Load Minix and start it, among other things. */
{
/* Initialize tables. */
initialize();
/* Get environment variables from the parameter sector. */
get_parameters();
while (1) {
/* While there are commands, execute them! */
while (cmds != nil) execute();
/* The "monitor" is just a "read one command" thing. */
monitor();
}
}
boot()は以下の処理を行う。
-
initialize()
関数を呼び出し初期化をする。 -
get_parameters()
パラメータセクタ(ブートストラップの次のセクタ)からパラメータを読み込む。 - パラメータにあるコマンドを
execute()
を呼び出して実行(OSの起動も含む)する。 - OSが終了したら、
monitor()
を呼び出し、プロンプト(d0p0s0>
等)を出力してコマンドを読み込む。
initialize(void)
initialize()は以下の処理を行う。
- ブートモニタを低メモリ(mem[0])の上端に移動する。低メモリとは通常640KiB未満のメモリのことだ。
- カーネルに渡されるメモリマップからブートモニターを削除する。これにより、カーネルはブートモニターが占有するメモリを割り当てることができなくなる。
- bootdevを初期化する。
ブートモニタの移動
/* Copy the boot program to the far end of low memory, this must be
* done to get out of the way of Minix, and to put the data area
* cleanly inside a 64K chunk if using BIOS I/O (no DMA problems).
*/
u32_t oldaddr= caddr;
u32_t memend= mem[0].base + mem[0].size;
u32_t newaddr= (memend - runsize) & ~0x0000FL;
(memend - runsize)
でコピー先の末尾が640KiBになるように仮の開始アドレスを計算する。~0x0000FL
は0xFFFFFFF0L
であり、論理積を取ってセグメントの16バイトアライメントを取っている。
#if !DOS
u32_t dma64k= (memend - 1) & ~0x0FFFFL;
/* Check if data segment crosses a 64K boundary. */
if (newaddr + (daddr - caddr) < dma64k) newaddr= dma64k - runsize;
#endif
データセグメントが64KiBバウンダリをまたぐ場合は再調整する。
/* Set the new caddr for relocate. */
caddr= newaddr;
/* Copy code and data. */
raw_copy(newaddr, oldaddr, runsize);
raw_copy()
(アセンブラで定義)を呼び出して、コードを低メモリの上端にコピーする。
/* Make the copy running. */
relocate();
relocate()
(アセンブラで定義)を呼び出して、コピー先で続きを実行する。
カーネルに渡されるメモリマップからブートモニターを削除
if (mon_return = (mem[1].size > 512*1024L)) mem[0].size = newaddr;
拡張メモリがある場合、つまり(mem[1].size > 512*1024L)
の場合は、mem[0]からブートモニタのメモリの領域を除く。
mem[0].base += 2048;
mem[0].size -= 2048;
BIOS割込みベクタや、BIOSのデータ領域、および古いカーネルのヘッダ領域のために、開始アドレスを2KiBずらす。
bootdevを初期化する
/* Find out what the boot device and partition was. */
bootdev.name[0]= 0;
bootdev.device= device;
bootdev.primary= -1;
bootdev.secondary= -1;
で初期化しておいて、get_master(master, table, masterpos)
の結果を使って値を設定していく。
raw_copy(mon2abs(&lowsec),
vec2abs(&rem_part) + offsetof(struct part_entry, lowsec),
sizeof(lowsec));
他にも、アクティブパーティションの開始セクタをC言語の領域にコピーする。(rem_part
はアクティブパーティションのパーティションテーブルのエントリ位置。lowsecはパーティションの開始セクタ)
get_parameters(void)
// 略
b_setvar(E_SPECIAL|E_VAR|E_DEV, "rootdev", "ram");
// 略
/* Default hidden menu function: */
b_setenv(E_RESERVED|E_FUNCTION, null, "=,Start MINIX", "boot");
のように、変数、環境変数を設定し、
if ((r= readsectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) {
アクティブパーティションの2番目のセクタから起動用のコマンド文字列を取得する。
acmds= tokenize(&cmds, params);
// 略
(void) tokenize(acmds, ":;leader;main");
コマンド文字列は、tokenize()
でトークン化され、cmds
にリンクリストで追加される。
execute(void)
cmd
にコマンドがある時はexecute()
が呼ばれ、コマンドが実行される。
name= cmds->token;
res= reserved(name);
で、token
が"boot"の場合、R_BOOT
がresに代入される。
switch (res) {
case R_BOOT: bootminix(); ok= 1; break;
R_BOOT
の場合にbootminix()
(bootimage.cにある方。このファイルにある方ではないので注意)が呼ばれ、MINIXが起動する。