C. 12 List5 . カーネルのマスター makef ⅱ e のサンプル KERNBASE=FEØØØØØØ . endif # パーツが見つからなければコンパイルディレクトリをセットアップする。 . if ! exists (machine) F00 ! = 1 Ⅱ—s $S/incIude/$ (MACHINE) machine . endif # メインターゲット @${LD} -z -T ${KERNBASE} ー 0 $@ -X ${FIRSTOBJ} ${OBJS} vers. 。 @${LD} -z -T ${KERNBASE} ー 0 $@ -x ${FIRSTOBJ} ${OBJS} vers . 。 . MAIN: ALLMAN— KERNS = a11 ー ${MANI} ${MAN2} ${MAN3} ${MAN4} ${MAN5} ${MAN6} ${MAN7} ${MAN8} ${MAN9} KOBJS = ${FIRSTOBJ} ${OBJS} $ {KERNEL} . if defined(DDB) KERNS + = . endif $ {KERNEL} . ddb . if defined(PROFILE) _KERNS + = . endif ${KERNEL}. prof . if defined(KGDB) KERNS + = . endif ${KERNEL}. kgdb a11 : ${-KERNS} ${ALLMAN} assym. s : $S/inc1ude/sys/param. h $S/inc1ude/buf. h $S/inc1ude/vmmeter. h \ $S/inc1ude/proc . h $S/inc1ude/msgbuf . h machine/vmparam. h \ $S/config/genassym. c ${CC} ${INCLUDES} -DKERNEL ${IDENT} ${PARAM} ${BASE} \ $S/conf ig/genassym. c ー 0 genassym ・ /genassym >assym ・ s . include "$S/config/kerne1. kern. mk" . include "$S/config/kerne1. dev. mk" . include "$S/config/kerne1. fs . mk" . include " $S/conf ig/kernel . domain. mk" SRCS= ${KERN-SRCS} ${MACH-SRCS} ${DEV-SRCS} ${FS-SRCS} ${DOMAIN-SRCS} ${KERNEL} : Makefi1e symbols. sort ${FIRSTOBJ} ${OBJS} @echo loading $@ @$S/config/newvers.sh @${CC} -c ${CFLAGS} ${PROF} ${DEBUG} vers . c . if defined(DEBUGSYM) 563
AppendixC 大規模なアプリケーション開発のための動的 make 環境 . c . ${KMODULE}mo : ${MACH-C} . s . ${KMODULE}mo : ${MACH-AS} . 1 " く /tmp/dep >depend-mach ーく /tmp/dep >depend-kern C. 12 List 5 : カーネルのマスター makef ⅱ e のサンプル rm -rf /tmp/dep . kerno : sed —e S ; . 0 mv . depend /tmp/dep mkdep ${COPTS} $ {. ALLSRC} depend-mach : ${MACH-SRCS} rm -rf /tmp/dep . kerno : sed —e S ; . 0 mv . depend /tmp/dep mkdep ${COPTS} $ { . ALLSRC} depend—kern : ${KERN—SRCS} DEPEND + = depend_kern depend_mach ラ 5 。。ノ印ノ、最 # This make file incorporates the generic 386BSD kernel program makefile , # kernel . mk. Copyright (C) 199 の一 1994 W. J01itz, A11 Rights Reserved. . SUFFIXES: . symbols . 9 . 8 . 7 . 6 . 5 . 4 . 3 . 2 TOUCH?= CPP?= CC?= LD?= touch —f —c CPP C C /usr/bin/ld # ※過剰。ファイルべースあたりのインクルード手順の修正 INCLUDES= —I$S/inc1ude COPTS + = ${INCLUDES} ${IDENT} -DKERNEL ー Di386 DEPEND= depend_mk ASFLAGS= ${DEBUG} CFLAGS= ー 0 ${COPTS} 562 BASE= —DKERNBASE=Øx${KERNBASE} CFLAGS + = —DKERNBASE=Øx${KERNBASE} . if defined(KERNBASE) # 異なった仮想アドレスペースにカーネルを置くべきか ? —DNPX
6.3 kern / sig. c の関数とファイル構成 [ Fi1e:/usr/src/kerne1/kern/sig ・ c, line: 682 ] switch ( (int)p—>p-sigacts->ps-sigact [sig] ) { case SIG_DFL : * システムプロセスにはデフォルト動作を行わない if (p->p-flag & SSYS) line : 698 ] 現在評価中のシグナルの動作をチェックする。ます最初に、システムプロセス ( SSYS ) に対してデフォ ignore * / [ Fi1e : /usr/src/kernel/kern/sig ・ c , ルト動作 ( つまり終了 ) を行おうとしていたら、それを無視する。 break ; 337 * psig に処理させる。 / * このシグナルには動作がある。 default : [ Fi1e : /usr/src/kernel/kern/sig. c , line : 727 ] すべて評価した。 て処理されるように、そのシグナルの番号を示す値を返す。以上で、デフォルトで透過的なケースは、 視する。しかし、どちらのケースにも該当しないシグナルは配信しなければならない。 psig() に渡し によって生じたものであれば、無視のプロバティに残されているシグナルと同じく、そのシグナルは無 動作が、孤児のプロセスグループに接続されていない状態で、残されているプロセスに対する端末停止 関数 stop() を使ってプロセスを停止させ、シグナル動作をこの関数で完全に実行する。もしその停止 デフォルト動作が停止シグナルの送信であり、しかもプロセスがトレースされている場合、ヘルバー } e1se こには来ない return (sig) ; ignore * / break; / * * デフォルト動作は無視である * SIGCONT 以外では、 } else if (prop & SA-IGNORE) { break ; stop(), 1 , sig); ignore * / break; / * prop & SA-TTYSTOP) ) (p->p-pgrp¯>pg-j 0bc if (p->p-flag & STRC Ⅱ if (prop & SA-STOP) {
◆ 4.3.46.1 console-config() の実装 [ Fi1e : /usr/src/kernel/kern/config. c , cfg = *cons-cfg; / * コンソールが定義されているか ? * / if (config—scan(cfg, cons-cfg) return ( の ; 第 4 章カーネル内部サービス (kern/config. c 、 kern/malloc. c) = の = の 1122 ] はじめにコンソールのコンフィギュレーション文字列を評価して、共有コンフィギュレーション文字 列の中にコンソールが存在するかどうか判定する。もしコンソールの割り当てが定義されていなければ、 = の return ( の ; if (config-scan(cfg, mod-cfg) cfg = *mod—cfg; / * 要求されたコンソールモジュールは構成可能か ? * / return ( の ; if (cfg—string(cons—cfg, modnamel , sizeof (modnamel) ) [ Fi1e:/usr/src/kerne1/kern/config. c, line: 1129 ] このルーチンは即座に不成功を返す。 else if (strcmp(modnamel , arg) ! = の / * 同じモジュールか ? * / default_console_devif = dif ; = の if (strcmp(modnamel, "default") [ Fi1e : /usr/src/kernel/kern/config. c, line : 1138 ] 走査して、構成可能かどうか調べる。もしそうなら、このコンソールの構成は失敗に終わる。 システムに組み込んで使えるコンソールモジュールを取り出したら、与えられたモジュール文字列を ジュールを取得する。もしなければ、そのときも不成功を返す。 コンソールのコンフィギュレーション文字列の、その次のトークンから、コンソールとして使うモ return ( の ; コンソールとして構築すべきコンソールと違っていたら、それは正しいコンソールではないことがわか 変数に保存して、デフォルトコンソールが構築されたことを示す。そうでなく、もしそのコンソールが、 コンソールとして構成するモジュールが、デフォルトコンソールであれば、その値は別のグローバル 236 こまでに不成功となる状況にならなければ、構成すべき正しいデバイスを見つけたことになる。こ る。それはシステムの別のモジュールである。そのときは、この関数は不成功を返す。
第 3 章 CPU 固有のプリミティブ ( i386 / t 「 ap. c 、 i386/cpu. c) ナル (SIGBUS) を送る。現在のところ、ほかにバスエラーシグナルを生成する要因はないが、将来追加 される可能性はある。そこで、他の要因による SIGBUS と区別できるように、プロセッサソ ルドに BUS-ALIGN-TRAP のタグを付けておく。 [ Fi1e : /usr/src/kerne1/kern/i386/trap. c , line : 278 ] case T-DIVIDEI T-USER: ucode = FPE_INTDIV_TRAP ; signo = SIGFPE; break; ースフィー 小数点のシグナルが使われていたからである。昔からの慣習が、しぶとく生き残っているのだ。 グナルで兼用されている点に注意してほしい。これは PDP ー 11 には整数除算命令がなく、例外には浮動 による整数除算であるのに、 0 による整数除算を示す標準のシグナルが存在せす、浮動小数点演算のシ 0 による整数除算であることを示すコード、 FPE- 工 NTDIV-TRAP が付加される。問題を起こしたのが 0 は浮動小数点演算シグナル ( SIGFPE ) が送られる。このシグナルはさまざまな用途で使われているので、 X86 は 0 による整数除算を命じられると T ー D 工 VIDE トラップを発生する。これによって、プロセスに [ Fi1e : /usr/src/kerne1/kern/i386/trap. c , line : 288 ] ルトを返す。 数を呼び出して、そのフォールトの性質を取得する。そして、マシンおよびコプロセッサに特有のフォー X86 の浮動小数点演算コプロセッサが例外を起こした場合、コプロセッサドライバのエラーデコード関 break; signo = SIGFPE; ucode = npxerror() ; case T—ARITHTRAP IT-USER : [ Fi1e : /usr/src/kerne1/kern/i386/trap. c , line : 283 ] case T_PAGEFLT : case T—PAGEFLT ー T—USER : vm_offset_t va; / * カーネルページフォールト * / / * ューザーベージフォールト * / struct vmspace *vm = の ; vm—map—t map ; extern vm—map—t kernel—map ; unsigned nss eva = rcr(2); va = trunc-page ( (vm-offset-t) eva) ; 122
第 9 章 POSIX オペレーティングシステム機能 (kern/execve. c 、 kern/descrip. c) *pop & UF-EXCLOSE ; *retval return ( の ; ファイル記述子のフラグの内容 (close-on-execute を除く ) を返す。 [ Fi1e : /usr/src/kernel/kern/descrip. c , line : 214 ] case F_SETFD : *pop = (*pop & ~ UF-EXCLOSE) ー (uap->arg & 1 ) ; return ( の ; ファイルのフラグを設定する。ただし close ー on ー execute フラグの内容は保存される。 [ Fi1e:/usr/src/kerne1/kern/descrip. c, line: 218 ] case F_GETFL : *retval return ( の ; fcntl() のファイル記述子引数で指定されたファイル記述子の、 返す。 OFLAGS(fp->f-f1ag) ; [ Fi1e : /usr/src/kernel/kern/descrip. c , line : 222 ] case F_SETFL : fp—>f—flag & = NFCNTLFLAGS ; ファイルインスタンスのフラグを fp—>f—flag ト FFLAGS (uap—>arg) & FCNTLFLAGS ; tmp = fp—>f-flag & FNONBLOCK; (*fp->f-ops->fo-ioctl) (fp, error if (error) return (error) ; tmp= fp->f—flag & FASYNC; (*fp->f-ops->fo-ioctl) (fp, error if ( ! error) return ( の ; fp—>f-flag & = &FNONBLOCK; tmp= の ; (caddr-t)&tmp, p) ; (caddr-t)&tmp, p) ; (caddr-t)&tmp, p) ; FIOASYNC, FIONBIO, (void) (*fp—>f—ops—>fo-ioctl) (fp, FIONBIO, return (error) ; 480 (fo-ioctl) メソッドで、ファイルインスタンスと、その下位オプジェクトの状態の変化を示す。もし については、下位のファイル的オプジェクトに問い合わせる。このために使うのは低いレベルの ioctl る。ノンプロッキング I / O を指定するフラグ ( FNONBLOCK ) と、非同期 I / O を指定するフラグ ( FASYNC ) 設定が許されるファイルフラグを、指定されたファイル記述子に結び付けられているファイルに設定す
第 9 章 POSIX オペレーティングシステム機能 (kern/execve. c 、 kern/descrip. c) 新しくコピーするファイル記述子の情報の内容を保持するため、新しいファイル記述子データ構造を 動的に割り当てる。引数のように、プロセスが供給するものからエントリの内容がコピーされ、そして 参照カウントを 1 であると主張する * 訳注 3 [ Fi1e:/usr/src/kerne1/kern/descrip. c, line: 1191 ] VREF(newfdp—>fd—cdir) ; if (newfdp—>fd-rdir) VREF (newfdp—>fd-rdir) ; 元のプロセスのカレントディレクトリを記述する vnode に参照数を追加する。これにより、コピーの 新しい参照が記録される。ルートディレクトリの vnode も使われていたら、そのルートディレクトリの vnode 参照数もインクリメントする。 [ Fi1e : /usr/src/kernel/kern/descrip ・ c , line : 12 の 1 ] if (newfdp—>fd—lastfile く NDFILE) { ((struct fi1edescØ * ) newfdp)—>fd—dfiles; newfdp—>fd—ofiles newfdp—>fd-ofileflags ( (struct fi1edescØ * ) newfdp) —>fd-dfileflags ; i = NDFILE; } else { コピー元である古いプロセスで現在オープンされているファイルの数が、ファイル記述子構造体に現 在存在する配列でまかなえる大きさであれば、ファイルポインタ配列とファイルフラグ配列を、新しく 割り当てられたファイル記述子の中のフィールドを指すように調整し、ファイル記述子の数には、デフォ ルトで割り当てられるグループを示す定数値 ( NDF 工 LE ) を設定する。 [ Fi1e : /usr/src/kernel/kern/descrip. c , line : 1212 ] newfdp—>fd—nfiles ; 1 while (i > 2 * NDEXTENT-&& i > = newfdp—>fd—lastfile * 2 ) i / = 2 ; MALLOC(newfdp->fd ofiles , struct file * * , i * OF 工 LESIZE, M_FILEDESC , M_WAITOK) ; (char * ) &newfdp—>fd—ofiles [i] ; newfdp—>fd—ofileflags そうでなければ、使用されるファイルの数を、テープルの大きさとして想定すべき値に最も近い NDEXTENT 単位のファイル数にする。これによって、短期間に大量のファイル記述子を使用するプロセ * 訳注 3 520 新しいファイル記述子データ構造を動的に割り当て、引数で与えられたコピー元のファイル記述子か ら内容をコピーし、参照カウントを 1 に設定する。
4.3 386BSD のコンフィギュレーション : kern/config. c 4.3.42 ldiscif-qsize() とは何か デフォルトのキューサイズである 0 を返す。これは、東縛されていないラインディシプリンのキュー長 ディシプリンの qsize ( ) メソッドを呼び出す。もしラインディシプリンが割り当てられていなければ、 もので、キューのキャラクタ数は実装に依存する。 ldiscif-qsize() は、割り当てられているライン イズを返す。この関数は、カーネルのラインディシプリン援助関数が、キューのサイズを知るために使う ldiscif-qsize() は、ラインディシプリンの実装に依存する、キャラクタを要素とするキューのサ return ( の ; return ((lif->li-qsize) (tp, q)) ; if (lif) [ Fi1e:/usr/src/kerne1/kern/config. c, line: 1 の 19 ] ◆ 4.3.42.1 ldiscif_qsize() の実装 不明確なポインタである。戻り値は、ラインディシプリンの qsize ( ) が返した値である。 ldiscif-qsize() の引数は 2 つ、端末構造体へのポインタと、その構造体の中の端末キューを指す ldiscif—qsize(struct tty *tp, void (q) { int [ Fi1e : /usr/src/kernel/kern/config. c , line : 1 の 14 ] は常に 0 ( 空 ) であることを示す。 231 録済みのラインディシプリンインターフェイスとして使えるようにする。 したシンポルによるパラメータを割り当てることを期待する。そして、それをカーネルに挿入して、登 を受け取り ( また、オプションで構成し ) 、指定のラインディシプリンインターフェイスに、引数で指定 プリンは、 ldiscif-config() が、共有コンフィギュレーション文字列を使って、ラインディシプリン れる。この関数は、ラインディシプリンのモジュール初期化工ントリから呼び出される。ラインディシ ldiscif-config() は、カーネルで使用できるようにラインディシプリンを登録するために使用さ 4.3.43 ldiscif-config() とは何か であることを示す 0 の値を返す。 ソッドを呼び出し、その値を返す。もしラインディシプリンが割り当てられていなければ、キューが空 端末構造体にラインディシプリンが割り当てられていたら、そのラインディシプリンの qsize ( ) メ
第 6 章 シグナルを介したプロセス例外 ( kern / sig. c ) ps->ps-stopsig = の ; シグナルが通常のユーザープロセススタックで実行されるように、シグナルスタックの機能を無効化 する。また、古いシグナルスタックポインタと状態の値もクリアして、この機能が有効になっても不正 なメモリ位置を指すことがないようにする * 39 。これはシグナルスタックフレームの誤用によって、シグ ナルスタック機能が偶然有効になるケースを予防する。最後に、ストップシグナルの値をクリアして、 最初にトレースが行われたときに、前のプロセスのコンテクストから残されたシグナルが間違って記録 されないようにする。 6.3.19 pgsignal() とは何か pgsignal() は、プロセスグループのリストを走査し、 psignal() によってシグナルをグループの 各プロセスに配送するカーネル内部関数である。オプションの引数によって、制御端末 ( コンソール ) を 持つプロセスだけに配送を制限することができる。 [ FiIe:/usr/src/kerne1/kern/sig ・ c, line: 1 のの 1 ] void pgsignal (struct pgrp *pgrp , int sig , int checkctty) pgsignal() の引数は 3 つ、プロセスグループへのポインタと、配送すべきシグナルと、制御端末に 接続されているプロセスだけにシグナルの配送を制限する ( たとえば、このプロセスグループの中で、 バックグラウンドで動くデーモンが開始された可能性のある ) オプションである。戻り値は返さない。 ◆ 6.3.19.1 pgsignal() の実装 [ Fi1e : /usr/src/kernel/kern/sig. c , if (pgrp) for (p = pgrp->pg-mem; if (checkctty line: 1 のの 6 ] p ! = NULL ; p = p->p_pgrpnxt ) = のⅡ p->p-flag & SCTTY) psignal(), sig) ; もし指定のプロセスグループが存在したら、そのグループの中のすべてのプロセスを順に調べる。も し制御端末オプションが指定されていてプロセスに制御端末 ( SCTTY ) があるか、あるいはオプションが 指定されていなければ、 psignal() 関数によりプロセスにシグナルを送る。 350 * 39 たとえばプログラムのアドレス空間の外に命令空間があり、エラーを生成する。
3.2 CPU 関連のカーネル機能 : i386/cpu. c 相対オプジェクトを参照しないことがわかっているからである * 47 。ただし、フレームボインタ相対の変 数 ( C 言語の auto 変数 ) は、まだアクセス可能であることに注意してほしい。 [ FiIe : /usr/src/kerne1/kern/i386/cpu ・ c, line : 185 ] / * if (p->p-stksz > = NBPG) * / kmem-free (kernel—map, (vm-offset-t)p—>p-addr, p—>p—stksz) ; free (p->p-addr, M-TEMP) ; * / このスレッドに割り当てられたスタックと PCB は、もう必要ではないので、これらを含むスレッドス テートを解放する。 auto 変数はこのフレームの中にあるので、この呼び出しを行ったあとは、もうアク セスできない。スピードよりもサイズが問題な場合、もしスレッドステートが、たった 1 ページのサイ ズであれば、 free() によって malloc ( ) に空間を返すことになる。そうでなければ、もっと高速だが アロケートするサイズの粒度が大きい仮想記憶システムのアロケータを使う * 48 。 [ Fi1e : /usr/src/kerne1/kern/i386/cpu. c, line : 191 ] final—swtch() ; リソースをすべて剥奪され、カーネルの共用リソース ( アドレス変換とカーネルプログラム ) または終 了スレッド専用のステート (PCB とカーネルスタック ) だけで実行している状態で、スレッドコンテク スト切替ルーチンが呼ばれ、もう二度と戻らない。次のスレッドへのエントリ時に、静的なステートが 解放され、それによってスレッドの終了が完結する。 ◆ 3.2.5.2 386BSD における cpu ー texit ( ) の開発履歴 386BSD の古い実装では、実行中のプロセスからステートすべてを解放することができず、もうひと つの関数、 cpu-wait() を使って、呼び出し側プロセスのステートの残りを解放していた。その理由は、 ひとつにはプロセスごとの情報を固定の仮想アドレスに置くことによってすばやく切り替えようという もので、カーネルスタックがユーザーの仮想アドレス空間に残されていたためである。これは UNIX シ ステムと同じなのだが、ユーザーアドレス空間は、もう必ずしもカーネルと独立していないので、これ ではプロセスの生成と破壊が非常に複雑になってしまう。 ◆ 3.2.5.3 386BSD における cpu ー texit ( ) の設計に関する選択肢とトレードオフ この実装は、仮想記憶システムおよびその他のサプシステムとの相互作用を最小限に留めるのが目的 である。また、スレッドステートは、親が状態を収拾するまで放っておくのではなく、実行中のスレッ / * else * 48 注意 : カーネルのスレッド生成では、スレッドステートは同様にアロケートされる。 * 47 古いスタックの内容は、もう必要ないのでコピーしない。 157