前章までで,Xinu を起動するための最小構成となる要素を説明した.今回は, さまざまなデバイスを接続するための機構,さらに,OS の全ての層を初期化する 手続きについて説明する.
さまざまな デバイス(device)(装置) を OS に接続したり, 自由に OS から切り離しをすることができなければならない.つまり, ある特定のデバイスに「依存」した OS は,よい設計とはいえない. 例え,「キーボード」(これもデバイスの一つ) がなくても,OS の機能を 果たさなければならない.そのためには,デバイスへの入出力を一般化する必要が ある.
デバイスへの入出力は,UNIX 系のOS,Xinu などでは,「ファイルへの入出力」 として一般化される.より低レベルのハードウエアとしては, 「入出力インターフェース」を介して,入出力(通信) を行う. まず,入出力インターフェースをまとめておこう.
シリアル通信(serial communication)は,1本の信号線に,複数の 情報を,順番に受渡す通信を指す.パラレル通信(parallel communication) は,複数の信号線に,同時に複数の情報を流す通信をさす.各々長所と 短所がある.
シリアル通信の長所を示す.
短所は,以下の通りである.
パラレル通信は,シリアル通信とは,全く逆の性質を持つ.シリアル通信の 通信の速度が,パラレル通信と比較して,遅いとしても,技術の進歩により, シリアル通信としては,高速になる傾向があるので,多くの デバイスの通信で用いられている.また,キーボードなど,比較的 通信速度が遅くても機能を果たすデバイスは,古くよりシリアル通信を用いている.
コンピュータの背骨にあたる信号線を,バス という.コンピュータの 処理と密接に関わり,高速の通信をしながら処理を進めるには,バスに直結した 通信が非常に有効である.しかし,バスは,コンピュータが処理している間, 常に,使われているのでパスに直結し,通信をするのは容易ではない. ノート型 PC が開発され,携帯品としてコンピュータが用いられるようになって, 動的に(コンピュータの電源をいれたまま),バスと通信をする方法が開発された. それが,PCM/CIA カード である.または,ブリッジ(bridge) という. この仕組みを用いれば,コンピュータを停止させることなく, さまざまな,デバイスを,パスにつなぐことができる.PCM/CIA カードに, 入出力インターフェースを搭載することにより, このインターフェースを通した通信も可能となる.
例えば,「マウス」は,NEC PC9801 など,初期の PC では,バス直結型の 通信が行われたが,マイクロソフトが,シリアル通信のマウスの規格を発表して 以来,シリアル通信によるマウス制御が当たり前となった.キーボードも, 最も初期のキーボードの線は非常に太く,パラレル通信であったが, 「長く信号線を伸ばしたい」という欲求から,シリアル通信が当たり前となった. フロッピディスク装置をはじめとして,ハードディスクなどは,CPU,メモリ 間で高速の通信をする必要があるので,最近では,PCM/CIA カードを使った パスへ直結する通信が,開発されている.
Xinu のデバイス管理は,以下のファイルから構成されている.
例えば,キーボード(keyboard) を制御する関数は,以下の通りである.
このように,各デバイスに対して,
を記述することができる.各デバイスは,入力専用デバイス,出力専用デバイス, 双方向デバイスなど,さまざまなデバイスがあるので,上の関数群全てを 用意するとは限らない.その場合は,ionull 関数や,ioerr 関数を 設定する.
これまでに,Xinu を実行するために必要な,最低限の階層を説明した. ここでは,各階層に初期化処理をするために,どのような処理が必要かを 紹介する.
以下のファイルに記述されている.
以下に,順に説明する.
2 章「メモリ管理」で説明したプログラムを初期化するプログラムは 以下のようになる.
char *maxaddr; /* max memory address (set by sizmem) */ struct mblock memlist; /* list of free memory blocks */
/* initialize free memory list */ /* PC version has to pre-allocate 640K-1024K "hole" */ if (maxaddr+1 > HOLESTART) { memlist.mnext = mptr = (struct mblock *) roundmb(KEND); mptr->mnext = (struct mblock *)HOLEEND; mptr->mlen = (int) truncew(((unsigned) HOLESTART - (unsigned)KEND)); mptr->mlen -= 4; mptr = (struct mblock *) HOLEEND; mptr->mnext = 0; mptr->mlen = (int)truncew((unsigned)maxaddr - HOLEEND - NULLSTK); } else { /* initialize free memory list */ memlist.mnext=mptr=(struct mblock *)roundmb(KEND); mptr->mnext = 0; mptr->mlen=(int)truncew((unsigned)maxaddr-(int)KEND - NULLSTK); }
3 章「プロセス管理」で説明したプログラムを初期化するプログラムは以下のように なる.
/* Declarations of major kernel variables */ struct pentry proctab[NPROC]; /* process table */ int nextproc; /* next process slot to use in create */
for (i=0 ; i<NPROC ; i++) /* initialize process table */ proctab[i].pstate = PRFREE; pptr = &proctab[NULLPROC]; /* initialize null process entry */ pptr->pstate = PRCURR; for (j=0; j<7; j++) pptr->pname[j] = "prnull"[j]; pptr->plimit = (WORD)(maxaddr + 1) - NULLSTK; pptr->pbase = (WORD) maxaddr - 3; pptr->pesp = pptr->pbase-4; /* for stkchk; rewritten before used */ *( (int *)pptr->pbase ) = MAGIC; pptr->paddr = (WORD) nulluser; pptr->pargs = 0; pptr->pprio = 0; currpid = NULLPROC;
「レディリスト」の初期化は以下のようになる.
rdytail = 1 + (rdyhead=newqueue()); /* initialize ready list */
4 章「同期処理」で説明したプログラムを初期化するプログラムは,以下のように なる.
for (i=0 ; i<NSEM ; i++) { /* initialize semaphores */ (sptr = &semaph[i])->sstate = SFREE; sptr->sqtail = 1 + (sptr->sqhead = newqueue()); }
5 章「実時間管理」で説明したプログラムを初期化するプログラムは, clkinit 関数 である.
#ifdef RTCLOCK clkinit(); /* initialize r.t.clock */ #endif
6 章「高レベルメモリ管理」で説明したプログラムを初期化するプログラムは, initialize 関数では,記述されていない. 初期化するプログラムは,poolinit 関数であるが, 「メモリマーキング」を用いて,一度だけ呼び出されるように工夫されている. mkpool 関数を参照しよう.
7 章「プロセス間通信」で説明したプログラムを初期化するするプログラムは, initialize 関数では,記述されていない. 初期化するプログラムは,pinit 関数 であるが, 「メモリマーキング」を用いて,一度だけ呼び出されるように工夫されている. pinit 関数を参照しよう.
本章で説明した「デバイス管理」を初期化するプログラムは,init_dev 関数 をデバイスの数の分だけ呼び出せばよい.
#ifdef NDEVS for (i=0 ; i<NDEVS ; i++ ) { init_dev(i); } #endif
最初に起動されるプロセスは,以下のように記述する.この初期化プログラムは, "NULLPROC" となって,他の全てのプロセスが終了するのを待つ.
/* create a process to execute the user's main program */ userpid = create(main_xinu,INITSTK,INITPRIO,INITNAME,INITARGS); resume(userpid); while (TRUE) /* empty */;
最後に,起動メッセージが出力される.以下のようになる.
sprintf(vers, "Simplified Version of PC Xinu %s",VERSION); kprintf("\n\n%s\n", vers); if (reboot++ < 1) kprintf("\n"); else kprintf(" (reboot %d)\n", reboot); kprintf("%d bytes real mem\n",(unsigned long) maxaddr+1); #ifdef DETAIL kprintf(" 0x%x", (unsigned long) 0); kprintf(" to 0x%x\n", (unsigned long) (maxaddr) ); #endif kprintf("%d bytes Xinu code\n",(unsigned long)((unsigned long)&end- (unsigned long) start)); #ifdef DETAIL kprintf(" 0x%x", (unsigned long) start); kprintf(" to 0x%x\n", (unsigned long) &end ); #endif #ifdef DETAIL kprintf("%d bytes user stack/heap space\n", (unsigned long)((unsigned long)maxaddr- (unsigned long) &end)); kprintf(" 0x%x", (unsigned long) &end); kprintf(" to 0x%x\n", (unsigned long) maxaddr); #endif kprintf("clock %sabled\n", clkruns == 1?"en":"dis");