struct - みる会図書館


検索対象: UNIX MAGAZINE 1998年3月号
11件見つかりました。

1. UNIX MAGAZINE 1998年3月号

連載 / BSD をハックする一① 図 2 namei() 友粋 * Overa11 outline of namel : ( 略 ) / * Convert a pathname intO a pointer tO a locked inode . while ( !done & & ! error) { get starting directory C opy 1 Ⅱ name if symbolic link , massage name in buffer and continue caII lookup t0 search path. int namei (ndp) register struct nameidata *ndp , register struct filedesc *fdp; register char *cp, register struct vnode *dp , ( 略 ) / * pointer tO file descriptor state * / / * the directory we are searching * / / * pointer intO pathname argument * / struct componentname *cnp = &ndp—>ni—cnd ; ndp—>ni—cnd. cn—cred = ndp—>ni—cnd. cn—proc—>p—ucred; fdp = cnp->cn—proc—>p—fd; ndp—>ni—loopcnt / * Get starting point for the translation. if ( (ndp->ni—rootdir = fdp—>fd—rdir) = = NULL) ndp—>ni—rootdir dp = fdp—>fd—cdir; VREF (dp) ; for ( ; / * Check if root directory should replace current directory. ( 略 ) * Done at start of translation and after symbolic 1ink. cnp¯>cn—pnbuf ; cnp— > cn—namept if ( * (cnp—>cn-nameptr) = = vrele(dp) ; while ( * (cnp—>cn—nameptr) cnp¯>cn—nameptr 十十 ; dp = ndp->ni-rootdir; VREF (dp) ; ndp—>ni—startdir = dp ; if ((error = lookup(ndp)) ! = 0 ) { ndp->ni-pathlen- FREE(cnp—>cn-pnbuf , M_NAMEI) ; if ((cnp->cn—flags & ISSYMLINK) / * Check for symbolic link * / return (error) ; ( 略 ) 図 3 struct proc ( 抜粋 ) struct proc { struct proc *p—forv; struct proc *p_back; LIST_ENTRY(proc) p_list ; / * substructures: * / struct pcred *p—cred ; struct filedesc *p—fd; struct pstats *p—stats ; ( 略 ) 72 / * Doub1y—Iinked run/sleep queue. / * List of a11 processes. * / / * process owner's identity. * / / * Accounting/statistics (PROC ONLY) . / * ptr to open files structure. * / UNIX MAGAZINE 1998.3

2. UNIX MAGAZINE 1998年3月号

連載 / BSD をハックする一① 図 4 struct filedesc ( 抜粋 ) struct filedesc struct char struct struct ( 略 ) file **fd_ofiles ; *fd_ofileflags; vnode *fd_cdir ; vnode *fd_rdir ; fi1e structures for open files * / / * per¯process open file flags * / current directory * / root directory * / ・キャラクタ・デバイス ・プロック・デバイス ・名前付きパイプ ・ UNIX ドメインのソケット ・ディレクトリ UNIX MAGAZINE 1998.3 こでは詳しく説明しませ いることに注目してください。 ルート・ディレクトリの情報 ( fd ー rdir ) もここに入って 関する情報 (fd-cdir) も入っています。 はなく、プロセスのカレント・ワーキングディレクトリに 幸肋財褓内されている構造体です ( 図 4 ) 。しかしそオけごけで がオープンしているファイル・ディスクリプタに関する情 filedesc 型のポインタになっています。これは、プロセス → p-fd がなんであるかは、図 3 を見てください。 struct セス (struct proc) へのポインタを得ます。その先の cnp->cn-proc で、この namei 操作をおこなうプロ ます。 する vnode ポインタ ) に置き換えるという処理をしてい ス名のう頁が / ならは、ルート・ディレクトリ ( に対応 実際の処理が始まる部分で fd-cdir を dp に設定し、パ ペアで inode も得られます。 の UFS など ) に関して vnode を得たときには、それと します。ただし、 inode を用いるファイルシステム ( 通常 ていますが、じつは現在では vnode ( へのポインタ ) を返 るカーネル内関数です。「麪頁のコメントでは inode となっ す。 namei() はパス名からそれカ甘嗣ーファイルの実体を得 図 2 は、 vfs 」 ookup. c のなかにある namei() の抜粋で ファイルシステム・サービスの内幕 マンドで設定できます。 通常のファイルと同じように chown 、 chgrp 、 chmod コ たとえば、装置に対するアクセス権を設定したけれは、 な属性をもっています。 は、通常のファイル ( レギュラー・ファイル ) と同しよう んが、 UNIX には chroot() システムコールがあり、あ るプロセスにとってのルート・ディレクトリを、そのシ ステムの本当のルート・ディレクトリではないディレクト リに設定できます。そのため、プロセスごとにルート・デ ィレクトリ情報をもつようになっているのです。 chroot() を使うと、あるプロセスとそのイ哥系のプロセスがアクセス 可能な範用を原理的に限定することかできます。 chroot() で変えたルート・ディレクトリは、 root 権限をもってし ても解除するガ去はありません。 inode と vnode BSD のカーネルを読むときに戸惑うのは、歴史自第緯 の景斧でプログラムが分かりにくくなっているところでし inode は UNIX のファイルの管理情報をオ褓内する構造 体です。昔の UNIX は 1 種類のファイルシステムだけ をサホートしていたので、ファイルシステム内のデータ構 造にもカーネル内のファイル関連の情報管理にも、 struct inode を使っていました。 その後 NFS の導入の際に複数のファイルシステムを 扱う必要匪が出てきたため、 inode を一ヨ殳化した vnode を使うようになりました。しかし、関数名などに inode の名前が残っていることがあります。たとえは、 namei( はパス名から inode を得る関数でした (path NAME to I node)o 今日ではパス名から vnode を得る関数になっ ていますが、関数名は以前のままです。 4.4BSD のローカル・ファイルシステム (FFS 、 LFS 、 MFS) を使用しているかぎりは、 vnode と inode は表裏 一体のものと考えてかまいません。図 5 は struct vnode の抜粋ですが、 UFS に関する vnode の v-data には、 struct inode へのポインタかオ翻タされています。 情報管理 UNIX カーネルがもつ inode 情報 (struct inode) は、 ハードディスクに f タされた inode 情報 (struct dinode) 73

3. UNIX MAGAZINE 1998年3月号

連載 / BSD をハックする一① 図 5 struct vnode ( 抜粋 ) と追加の情報を褓内するフィールドを備えた構造体です。 $NetBSD: vnode . h,v 1 .38 1996 / 02 / 29 20 : 59 : 05 cgd Exp $ ( 略 ) ( 略 ) @(#)vnode . h 8.11 (Berke1ey) 11 / 21 / 94 * The vnode is * vnode ( 略 ) struct vnode { u-long short short ( 略 ) ( 略 ) i nt 10 れ g enum VOid ( 略 ) the focus Of a11 file activity in UNIX. There is a allocated for each * each mounted—on file, text file, v-flag; v_usecount ; v_writecount ; (**v-OP) v—spare [ 7 ] ; vtagtype v—tag ; *v_data; active file, each current directory, and the root . private data for fs * / type 0f underlying data * / round t0 128 bytes * / VIlOde operations vector * / reference count Of writers * / reference count Of users * / / * vnode flags (see below) * / #define VCALL(VP,OFF,AP) VOCALL( (VP)->v-op, (OFF) , (AP)) #define VOCALL(OPSV,OFF,AP) ( ( *((OPSV) [(OFF)] ) ) (AP) ) 図 6 struct inode の内容 ( 抜粋 ) struct inode { struct struct ( 略 ) dev_t 1110_t ( 略 ) * The struct inode *i_next ; inode **i_prev; i_dev; i_number ; / * Hash chain forward . * / / * Hash chain back . * / / * Device associated with the inode . / * The identity of the inode . * / on—disk dinode itself . dinode i_din; / * 128 bytes of the on—disk dinode. * / UNIX のファイルシステムでは、ディスク領域はⅲ ode 領域とデータ領域に分けて管理されます。ファイルの属生 情報かオ褓内されたものが inode です。 1 つのファイルシ ステムのなかで、 inode とファイルは 1 対 1 て対応しま す。カーネル内では、ファイル名はディレクトリを検索し て inode 番号を得るためのキーにすぎません。 inode 領域は、 inode と呼ばれる構造のデータが並んだ 領域です。ディスク上の inode の形式は、 struct dinode で定義される 128 バイトのデータブロックです (ufs/ufs /dinode. h)o たとえば、 8 番の inode は inode 領域の先 頭から 1 , 024 バイト目以降の 128 バイトぶんに相当しま す。 1 セクタは 512 バイトなので、セクタ 2 ( 3 つ目のセ 74 クタ ) の : 頁 1 / 4 ということになります。 UNIX では、複数のファイルシステムを mount シス テムコールでつなぎあわせて、 1 つの大きなツリー状ファ イルシステムの世界を構築しています。そのため、カーネ ル内では、どのファイルシステムの inode なのかを把握 しておく必要があります。たとえは、 sdOa の inode 番号 8 のファイルと sd0g のⅲ ode 番号 8 のファイルは区別 できなければなりません。 また、ディスクからメモリ上に inode 情報をコピーし てきた場合は、その情報が何番目の inode だったかの情 報もどこか凵早しておく必要があります。 struct inode ( 図 6 ) は、 inode 情報をメモリ上 : ゴ苻早 するための構造体です。効率的にアクセスするためのハッ UNIX MAGAZINE 1998.3

4. UNIX MAGAZINE 1998年3月号

連載 /FreeBSD ノートー 図 4 /sys/i386/isa/psm ・ c / * ring buffer * / #define PSM_BUFSIZE typedef struct ringbuf { 256 int count ; int head ; int tai1; mousestatus_t } ringbuf—t ; static struct psm—softc { unsigned char buf CPSM_BUFS I ZE] ; state; KBDC int kbdc ; addr ; mousehw_t hw ; ringbuf—t queue ; mousemode_t dflt_mode ; mousemode_t mode / * Driver status information * / / * Mouse driver state * / / * I/O port address * / / * hardware information * / / * operation mode * / / * default operation mode * / / * mouse status * / unsigned char ipacket [MOUSE—PS2—PACKETSIZE] ; / * interim input buffer * / unsigned char opacket CPSM-BUFSIZE] ; int int int int inputbytes ; outputbytes ; out puthe ad ; button ; } *psm-softc [NPSM] ; / * driver state flags (state) #define PSM_VALID #define PSM_OPEN #define PSM_ASLP 0X80 1 2 / * device driver declarateion isa_driver / * output buffer * / in the input buffer * / in the output buffer * / / * points the head 0f the output buffer * / button state * / / * the latest / * # of bytes / * # of bytes / * Waiting for mouse data * / / * Device is open * / { psmprobe , psmattach , "psm" , FALSE } ; #define CDEV_MAJOR 21 static struct psmselect psmioctl , psmopen , Cdevsw psm_cdevsw = { psmclose, nostop , nommap , psmread, nullreset, NULL , nowrite , nodevt Ot t y , psm" /isa/psm ・ c) でこれをみてみよう ( 図 4 。簡略化のために 82 うになっている。 ドライバはそれぞれの状態を別個に保存して混乱しないよ ては同し鶤頁が複題リ用されることもあるが、デバイス は通常 1 個しカ咐かないので 1 になる。デバイスによっ がコンパイル時に決まるデバイスの数だが、 PS/2 マウス た、 psm-softc という構造体が定義されている。 NPSM -softc" を追加し 伝統に従ってドライバ名 psm" に ムの例はすべて 2.2.5-RELEASE からとっている。 構造体のメンバーを一部省略してある ) 。以下、プログラ / * 21 * / NULL , UNIX MAGAZINE 1998.3 psm-cdevsw は、デバイスドライバをカーネルに登録す バーとして登録するのに必要なデータである。その次の ISA ノヾスのメン プ名 isa-driver からも分かるように そのあとで定義されている構造体 psmdriver はタイ 組みを工早するために有効な友去である。 のように利用されているかを追っていくのもドライバの仕 この構造体に集約されている。この構造体のメンバーがど 保されていることが分かる。ドライバの管理するデータは ほか、入出力のバッフアとこれを管理するための情幸毓 構造体の中身を見てみると、ドライバやマウスの状態の

5. UNIX MAGAZINE 1998年3月号

ラムのなかでは複雑な部類に属します。山も丘は、構文解 析をおこなう部分を直接プログラムするのではなく、構文 だけを指定して別のプログラムで作る手法が一殳的です。 UNIX の yacc コマンドでは、言韶 ) 構造とそれに対応す int る処理を書いておけば、構文角斤ルーチンを自重加勺に作成 できます (yacc の詳細はオンライン・マニュアルや解説 書 [ 1 ] を参照してください ) 。 yacc がインストールされていないシステムでも、 byacc や bison など、同じ機能をもつ別のプログラムがありま 1 す 1 工 - ト・ - { "set" T_SET T_ SWITCH T_WHILE sizeof srchn / sizeof *srchn ・ nsrchn ご覧のように、 C シェルのキーワードと対応する値 (csh. h の構造体の定義の直後で定義されています ) が、キ ーワードの ASCII コード順に並んでいます ( 一部省略し ています ) 。酉改」の要素数は、 ( 酉改」の大きさ ) + ( 要素の大きさ ) という式で計算しています。配列の名前の前に * を付ける と、第 1 要素を表します。その大きさを使って計算して いるわけです。 探索 こで、 syn3 関数のなかで用いられていた srchx 関数 をみてみましよう。さきはど、 逐次探索 「引数として与えられた単語が、システムがあらかしめも っている単語表に含まれているかを調べる関 このようなデータを利用して値を探索しますが、 朴に作ると次のようになるでしよう。 と説明しました。このように、あるデータの集まりのな かから何かを探し出すのが、、探索 " です。 srchx は、配列 srchx (cp) char *cp , にオ褓内されたレコードのなかから文字列をキー ( 探索の対 象 ) として探索を実行し、該当するレコードに勺されて いる整数を返します。 まず、データ構造です。データの集まりは、構造体の集 合として (csh. h で ) 定義されています。 extern struct srch { char *S_name ; short s_value ・ このプログラムでは、う巨頁から順番に要素の数ぶんだけ srchn ロ ; extern int nsrchn; for ループを回りながら、 strcmp を使って要素のなかの この構造体には、キーとなる文字列のための s-name と キーと与えられた文字列か等しいかどうかを検査します。 返す値のための s-value の 2 つのフィールドがあり、 すべての要素を調べるので、これでみつからなけれは、デ れを酉改」として宣言しています。その直後にある nsrchn ータには与えられた文字列と一致するキーをもつものがな は、この酉冽の要素数を表す変数です。 いことになります。 配列に褓内する内容は、 init. c で定義されています。 この探索方法は、、逐刻架索 " と呼ばれます。配列の長さ をとすると、探索に失敗する場合にはル回の上交が、 struct srch srchn ロ 成功する場合には平均して ( ル + 1 ) / 2 回の上交がおこな われます。 逐次探索の改良 このプログラムでは、値がみつからなければかならす最 後まて調べます。しかし、データは配列へのオ褓叔こ整列 ごく素 struct srch *sp ; for ()p = srchn ; sp く srchn + nsrchn; sp + + ) { if (strcmp(cp, sp—>s—name)) continue; return (sp—>s_value) ; return ( ー 1 ) ; T_LET { "break" T_BREAK { "breaksw" T_BRKSW 2 bison は GNU ソフトウェアてすから、ⅲ fo ファイルもインスト ーノレ されているかもしれません。このドキュメントはよくできているので、参 考になります。 116 UNIX MAGAZINE 1998.3

6. UNIX MAGAZINE 1998年3月号

されているという性質を利用すると、より効率的な探索が 可能になります。たとえば、、、 f " を探しているとき、酉リ 中の単語が、 g " て始まってい川ま、それ以降を探しても目 的の単語はみつかりません。言い換えれは、整列させたと きに探したい単語よりも後ろになる単語をみつけたら、即 座に探索を終了してもかまわないわけです。この考え方を = 0 ) 利用したプログラムは次のようになります。 srchx (cp) char * cp ; struct srch int *sp; プログラミング・テクニック きます。配列の長さを N とすると、探索が失敗した場合に も平均で約 ( ル十 2 ) / 2 回の上交てすむからです。しかし、 現実にはこの去カ硬われることはあまりありません。 れよりも効率的な手法があるからです。 配列のなかの要素が順番に並んでいるという性質を利用 すれは、すこし手間をかけるだけで効抑勺な探去かま現 できます。これは、、二 . 分探索 " (binary search) と呼ばれ ます。 二分探索では、ます探すべき範囲 ( 最初は配列全体 ) の、、真ん中 " のレコードとキーとを上交します。キーと等 しければ探索は終了です。レコードの値のはうが大きかっ たら、探しているキーはそれよりも小さい値のグループに あることになります。逆に、レコードの値のはうが小さい 場合は、より大きい値のグループにあるはすです。このよ うにして、徐々に探危囲を狭めていけば、いつかは探し たい値に到達するか、探す値がなくなるはすです。 ここ分探索をプログラムにすると次のようになります。 ニ分探索 for ()p = srchn; sp く srchn if ((i strcmp(cp, sp—>s- continue ; if (i く 0 ) return ( ー 1 ) ; return (sp—>s—value) ; return ( ー 1 ) ; for 文の中身カ畯わっています。 2 つの文字列を上交す 取 e ) ) > の 十 nsrchn ・ る場合、 strcmp 関数は、、等しけれは 0 、等しくなければ ・両方か等しければ 0 ・前の文字列が大きけれは正の数 ・前の文字列が小さけれは負の数 0 以外 " を返すだけではありません。 UNIX MAGAZINE 1998.3 べて、探索に失敗した場合の性能をすこし上げることカそ この去を使えは、前項列に挙げたプログラムとくら おかないと、戻り値がおかしくなってしまいます。 の return 文かま行されます。ここに return 文を書いて ば、 for 文のなかの return 文は実行されす、 for 文のあと られた文字列が配列中のどの文字列よりも大きい値であれ なお、 for 文の則の return 文もまだ必要です。ケえ 0 であれば一致しているので、対応する値を返します。 こで、イゞみつからなかったことを示す一 1 を返します。 ことを示すので、それ . 以ま . 探素を続けても無未です。そ 配列中の文字列のほうが大きい、つまり探索がいきすぎた を調べにいきます。逆に、戻り値が負の数になった場合は、 としてえられた文字列 ) のはうが大きいので、次の要素 strcmp 関数の戻り値が正の数なら、前の文字列 ( 引数 ます。 を返します。ここでは、その戻り値を積に利用してい srchx(cp) char *cp ; struct srch struct srch int spl srchn ・ *spl, *sp2; *sp; sp2 = srchn + nsrchn; vhile (spl く sp2) { sp = spl + (sp2 ー spl) / 2 ; if ((i strcmp ()p , sp—>s—name) ) return (sp—>s—value) ; if (i く 0 ) sp2 SP ; else spl return ( ー 1 ) ; 117 よりも小さい " という条件になっています。これは、、、探 が分かります。本を括る while ループは、、、 spl が sp2 足しているために、ちょうど配列全体の次を指しているの コードを指します。 sp2 のネ月化で spl に酉改」の要素数を 含む ) を指し、 sp2 は探す範囲の最大のレコードの次のレ 変数 spl は探索範囲の最小のレコードにのレコードを

7. UNIX MAGAZINE 1998年3月号

case ) ( ) continue ; if ( 1 く 0 ) seterror (ERR—TOOMANYRP) ; continue ; 開き刮瓜に出合ったら括弧のレベル 1 をインクリメント し、閉凵刮瓜に出合ったらデクリメントします。最初に 1 を 0 に初期化しているため、開き括弧と閉し瓜の対応が とれているあいだは 1 は 0 になっています。開き括弧だ けを読んた漱態では、 1 は正の数になるはすです。負の数 になった場合は、開き括弧より閉じ瓜が多いことを示す のでエラーを設定しておきます。 & が虫て現れない場合の処理は、次のようになってい ます ( 誌面の都合長で折り返しています。以ー FI 司椥 文字ーを読み込んだときは、その直後の文字も一かどう —>next—>word, cas e ) > ) ・ / * fall into continue ; if (p—>word [ 1 ] continue ; p = p->next ; STRand) ) if (p—>next ! = p2 & & eq(p UNIX MAGAZINE 1998.3 単語を調べます。ここでは break と continue を使い分 ら、バックグラウンドのコマンド実行ではないため、次の るために break 文を実行します。また、次の文字も & な ます、括弧のなかにあるかを調べそうであれば無視す C011tinue ; if (p—>word[l] break ; if ( 1 ! = の 単語が & であるときは、次のような処理をします。 ときに & の処理をしないようにしてあります。 あれは・、その & まで読み込み、 for ループを次に実行する た場合は、さらに次の単語が & かどうかを調べます。 & で c e プロックにそのまま移ります。 > また ( 引を読み込ん の単言韶 ) 処理に移ります。それ以外の文字の場合は、次の かを調べ、そうであれは特別な処理は不要なのですぐに次 プログラミング・テクニック けているようにみえますが、実際には switch 文の終りと for 文の終りのあいだに文はないので、同し効果か得られ ます。 問題は、こを抜けたあとです。ここまでを終えた段階 で残っているのは、この関数で処理しなければならないバ ックグラウンド実行の並びです。 tl = synl(pl, p, flags); ます、引数として、、 pl , p" ()l から p まで ) を指定し、 & の前までを synl 関数を用いて角斤して冓造を作りま す大に、ここで返されたヨ冓造の型に応して彳をの処理 を変えていきます。 最初は、前の部分が ; 、 & & 、Ⅱで区切られたコマン ドの並ひです ( それぞれ NODE-LIST 、 NODE-AND 、 NODE-OR)0 これらの場合には、バックグラウンド実行 を示すフラグを妾付けるのではなく、いったん括弧で囲 まれたコマンドのように扱ってからバックグラウンドに回 すための処理を実行します。 if (tl—>t-dtyp = NODE_LIST Ⅱ = NODE_AND Ⅱ tl—>t—dtyp = NODE_OR) { tl—>t-dtyp (struct command * ) - t xcalloc(l , sizeof(*t) ) ; t—>t—dtyp = NODE—PAREN ・ t—>t—dflg = F_AMPERSAND ー F_NOINTERRUPT ; t—>t—dspr = t 1 ; tl = t ; 上に挙げた場合以外、つまり、通常のコマンド (NODE -COMMAND) 、括弧で囲まれたサプシェルで実行する コマンド (NODE-PAREN) 、パイプでつながれたコマ ンド (NODE-PIPE) については、バックグラウンド実 行をおこなうように則の冓造のフラグを定します。 else tl—>t—dflg ー = F-AMPERSAND ー F_NOINTERRUPT; 残りは、記号に続く右側の部分を : 冓造にする処理で す。ここでは、左の冓造と右のオ哥冓造を逐次実行する構 造を作り、右の冓造には残りの部分を syntax 関数て解 析した結果を入れます。 = (struct command * ) xcalloc(l , sizeof (*t) ) ; t t—>t_dcar = tl ; t—>t-dflg = 0 ; t—>t-dtyp = NODE_LIST ; 113

8. UNIX MAGAZINE 1998年3月号

連載 / BSD をハックする一① 図 7 実行中のファイルの削除 % cd /tmp % df FiIesystem 512 ー /dev/sdOe % cp /bin/sleep % df Fi1esystem 512 ー blocks /dev/sdOe % . /sleep 20 & [ 2 ] 371 % rm —f sleep % df Fi1esystem 512 ー blocks /dev/sdOe 266846 266846 266846 blocks Fi1esystem 512 ー blocks % df Done [ 1 ] % wait 371 pO I 0 : 00.01 % ps gxa lgrep sleep Used 24 Used 104 Used 104 Avai1 Capacity 0 % Capacity Avai1 Capacity 253398 253398 Avai 1 253478 . /sleep 20 . /sleep 20 Used Avai1 24 253478 Capacity /dev/sdOe 266846 気テープ装置などのテパイスに書き込むのも、同し write システムコールでおこなえます。 もちろん、カーネル内ではこの 3 つは別の機能なので、 ffs-write() 、 nfs-write() 、 stwrite() を呼び分けなければ なりません。 同様に、 FFS と NFS では open 、 rename 、 mkdir 、 rmdir 、 rename 、 link などすべてのファイルシステム操 作について、それぞれのファイルシステムごとの処理関数 を呼び出す必要があります。 これをシステムコールごとに判断していたのでは効率が 悪いので、 UNIX では関数へのポインタを多用していま す。たとえ tiwrite システムコールをおこなうと、その VCALL ()p , VOFFSET (vop-write) , &a) カ呼び出されます。これはマクロで、 VOP-WRITE(vp, uio, ioflag, cred) 内部では、 になります。 vp は書き込もうとしているファイルの ているマクロで、最糸勺には、 に展開されます。 VCALL は vnode. h のなかで定義され 76 vnode ポインタです。 Mount ed on /tmp VP->V-op は、ファイルシステム・タイプごとの操作関 数のホインタの酉リです。酉改」の 5 番目には、 write 操作 をおこなう関数へのポインタが入っているわけです。 このように操作関数群へのホインタを vnode にもたせ ることにすると、ファイルシステムごとに決まっている操 作関数群を検索する作業は、ファイルに対応する struct vnode を namei() で検索するときにだけおこなえばよい ことになります。しかも、 namei() で vnode キャッシュ がヒットした場合、対応する関数群の検索は不要です。 同じ FFS 上のファイルであっても、レギュラー・ファ イル、デバイス・スペシャルファイル、名前付きパイプで は、同し write 操作でも呼び出す関数を変えねばなりませ ん。ファイルシステムごとに操作数群は複数用意されて います。 vnode を得るときに、「 FFS の名前付きパイプ」 といった不鶤頁を詩ヾて、それに対応する関数へのポインタ 群を v-op ロ絲タしています。 次回は、テンヾイス・スペシャルファイルにアクセスし た場合のカーネル内の働きについて、説明を続けます。 ( さいとう・あきのり大阪大学 ) [ 赭文献 ] [ 1 ] Don Libes 、 sandy Ressler 著、坂本文監訳、福 0 夋博訳 fLife with UNIX 』、アスキー、 1990 年 Mounted 0 取 /tmp Mounted on /tmp Mounted on /tmp UNIX MAGAZINE 1998.3

9. UNIX MAGAZINE 1998年3月号

ントします。閉じ瓜をみつけたときの処理もほは 1 司しで すが、 1 はデクリメントしておきます。 1 に関する処理は、 syn0 などの場合と同様です。 リダイレクトの記号を読み込んだときには、コマンドの ための単語の並びではなくリダイレクト専用の部分に保存 するので、Ⅱに加えてはいけません。 これは、次のように 処理されます。 case ' > ' ・ case く ) ・ if ( 1 ! = の { if (specp) Ⅱ十十 ; continue ; if (p—>next = = p2) continue ; if (any(RELPAR, continue ; continue ; p¯>next— >word [ 0 ] ) ) 1 が 0 でないのは手刮瓜内を処理しているときですが、サ プシェルの実行であればその内側て解析すべきなので、す ぐに continue で次の単語の処理を始めます。括弧内であ っても specp が真になっている場合は、コマンドの一部 として登録する必要があるため、Ⅱをインクリメントしま す。次の p—>next" が p2 と等しい場合は、リダイレク トの記号はあるのにファイルカ甘旨定されていません。そこ で、 if 文でファイル名か特別な文字て始まっていないか を検査しています。それ以タ V ) 場合は、リダイレクトする ファイル名が正しく指定されているはすですから、ファイ ル名のぶんを調整するためにⅡをデクリメントします。 これら以タ P ) 文字のときには、まとめて次のように処理 continue ; 十十 ; continue ; if (!specp & & 1 ! = 0 ) default : します。 UNIX MAGAZINE 1998.3 mand 構本を割り当てておきます。また、引数の並びを 作成にとりかかります。ます、 t というポインタに c 。 m - この for ループを抜けると、イ冓造に」助日するノードの いので n はインクリメントしません。 す。その場合には、ここでコマンド行をイ尉寺する必喫はな 最初の if 文は、サプシェルでの実行を条件としていま プログラミング・テクニック ー尉寺するために av というポインタに領域を割り当てます。 そして、これをノードの t-dcom に代入しておきます。 if (n く 0 ) 取 = 0 ; = (struct command * ) xcalloc(l , sizeof(*t) ) ; av = (Char * * ) xcalloc( (size_t) (n + 1 ) , sizeof(Char * * ) ) ; t—>t_dcom n = 0 ; if (p2—>word [ 0 ] t—>t-dflg = F_NOFORK ; lp = 0 ; if 文では、解析しなけれはならない部分の直後に閉し括 弧がないかを調べています。閉し括弧があれば、サプシェ ルでの実行の最後のコマンドなので fork は不要になりま す。ここで、 n 、 lp 、 rp 、 I の 4 つの変数を調整していま す。 n は av 酉改」の何番目の要素に入れるのかを、 lp と rp はそれぞれ開き括弧と閉じ瓜があった場所を、そして 1 は手刮瓜の対応がとれているかどうかを示します。 この直後の for ルプでは、さきはどの for ループと似 た処理をしながら、作成した av にデータをオ内したり、 t にリダイレクト窈青報を埋め込んだりしていきます。 て特徴的なのは、 switch 文のなかで goto 文を使い、単 語の保存に関する部分をまとめている点です。これによっ て、通常の単語 (default ラベル ) はもちろん、括弧やリ ダイレクトとして解釈しないくや > も同し部分て処理して います。 for 文を抜けたら、おもにエラーがあった場合の処理を します。サプシェルでのコマンド実行も、ここで syno を 呼び出しておこないます。前回説明したように、ここても 再帰的なデータ構造を作るために関数を再帰的に呼び出し ます。 t 1 = 0 ; syntax から始まる一漣の関数群における処理は、、構文 解析 " と呼はれます。与えられた入力の構造を解釈し、そ れに応して適切な処理 ( 今回の場合であれば冓造の作 成 ) をおこないます。 C シェルの構文解析は、同様な処理をおこなうプログ 構文解析 115

10. UNIX MAGAZINE 1998年3月号

連載 / BSD をハックする一① シュチェーンのポインタをはじめ、いろいろな情報を内 するフィールドが並んでいます。そのなかはどに、ファイ ルシステムを識別するための i-dev と inode 番号を↑内 する i-number があります。 最後のフィールド i-din が、言求メディア ( ハードディ スク ) 上の inode 情報のコピーです。 ードリンクに対応するために、 in 。 de はリンクカウン トをもっています。通常のファイルはリンクカウント 1 ードリンクされるごとにリンクカウントは 1 すっ増 えます。 ディレクトリは、初期状態でも . があるので、リンク カウントは 2 て始まります。そのディレクトリの下にサプ ディレクトリを作ると、そのサプディレクトリの .. で リンクされるのでカウントが 1 増えます。 ファイルか存在するだけでリンクカウントは 1 あるとい うことに注目してください。操作している人間は、、、ディ レクトリ dir の下にファイル foo を作り、その後 bar と いう名則で f 。。へのハードリンクを作る " という概念を抱 きがちです。しかし、カーネル内では、、新規に作ったファ イル (inode 番号〇〇番 ) を foo という名前で inode 番号 ロロのディレクトリ (dir) にリンクする。その後、 inode 番号〇〇番のファイルを bar という名前でロロにリンク する " という扱いです。 vnode/inode キャッシュ UNIX の内部では、ファイルアクセスはつねにメモリ 上の vnode/inode へのポインタを介しておこなわれま す。 基本的には、これらはディスク上の inode 情報のキャ ッシュです。とくに ・オープン中のファイル ・実行中のプログラム・ファイル ・マウントボイント ・ルート・ディレクトリ ・いすれかのプロセスのカレント・ディレクトリ に対応した vnode/inode は、かならすキャッシュに載っ ているというになっています。 すなわち、ファイルシステムにアクセスするというこ とは、各ディレクトリやファイルの情報を格納した v- UNIX MAGAZINE 1998.3 node/inode を得て、利用カウント v-usecount を 1 に するということです。すでにキャッシュにある vnode に アクセスしたときは、 v-usecount を 1 だけ増やします。 アクセスし終えたら 1 だけ減らします。 たとえば、さきほどの namei() では、 vrele() でカウ ントを 1 減らし、 VREF ( ) で 1 増やしています。カウン トが 0 になるとフリーリストに入れられます。 ディスクから情報を struct inode や vnode に言冗み出 してくる関数は、直接的には用意されていません。ディス クからの情報読出しは、、、あるファイルに関する vnode キャッシュのポインタを得る " という関数のなかに隠蔽さ れています。 あるディレクトリからファイルか削除されると、ディ レクトリからそのファイルの情報か消されます。そして、 inode 内のリンクカウントが 1 減らされます。リンクカ ウントが 0 になると、どこのディレクトリを検索しても そのファイルの vnode/inode を得ることができなくなり ます。しかし、そのときにすでにそのファイルがオープン されていたら、 vnode/inode 情報はカーネル内に残って いますから、読み書きを続けることかできます。 1 つのプロセスがあるファイルを使い終ると、そのファ イルに対応する v-usecount が 1 つ減らされます。その ファイルを使用していた最後のプロセスか処理を終えると v-usecount が 0 になるので、対応する inode キャッシュ とともにこの vnode キャッシュはフリーリストに入れら れます。キャッシュされたⅲ。 de を解放する手続きのな かで、リンクカウントが 0 なら使用しているディスクプ ロックを解放するという操作がおこなわれます。 これを確認してみましよう。 図 7 ではます、 sleep コマンドを / tmp にコピーしてい ます。そして、バックグラウンドでこの sleep を実行して おいて、 /tmp/sleep を削除してしまいます。このとき、 ファイルシステムの空き領域は増えませんし、 ps コマン ドで見ても sleep は存在しています。 20 秒後に sleep プ ロセスカ鮗了すると、ファイルシステムの空き領域か増え ています。 抽象操作と具イ樂イ乍の対応 UNIX 是供する抽象化機能のおかげで、ローカルディ スクのファイルに書き込むのも、 NFS 上のファイルや磁 75