変数 - みる会図書館


検索対象: UNIX MAGAZINE 2004年4月号
62件見つかりました。

1. UNIX MAGAZINE 2004年4月号

デバッガの基礎知識 図 19 ブレークポイントの一キ酳畭 (gdb) b 9 Breakpoint 3 at 0X8048381 : (gdb) b 6 Breakpoint 4 at 0X8048368 : file samp1e3. c, file samp1e3. c, 特集 1 ine 9 . line 6 . (gdb) info break Num Type 3 breakpoint 4 breakpoint (gdb) d Disp Enb Address What keep y 0X08048381 in main at samp1e3. c : 9 keep y 0X08048368 in main at samp1e3. c : 6 De1ete a11 breakpoints? (y or n) y (gdb) info break NO breakpoints or watchpoints . (gdb) 図 20 main 謚にブレークポイントを言綻 (gdb) b main Breakpoint 5 at 0X8048368 : file samp1e3. c, line 8. (gdb) info break Num Type 5 breakpoint (gdb) Disp Enb Address What keep y 0X08048368 in main at samp1e3. c : 8 す。ここで、、 y " を入力すれば、最初からプログラムが実行 されます。 すると、最初のプレークポイントで実行が中断されます。 この段階では、まだ変数 buf と p か亘言されているだけで す。そこで、これらの変数がメモリ上のどの領域に確保さ れたのかを調べてみます。 print コマンドに buf や p という変数をそのまま指定す ると、その変数の内容が表示されてしまいます。ここで表 示したいのはアドレスですから、 print コマンドに変数の アドレスを指定します。 (gdb) p &buf [ 0 ] $ 1 = 0xbfffe6c0 " \ 024 \ n \ 023B ( (gdb) p &p (char * * ) Oxbfffe6bc (gdb) 変数 buf はメモリ上の Oxbfffe6c0 というアドレスから 始まる領域に、変数 p は同じく 0xbfffe6bc に確保されて いることカ認できます ( 図 21 ) 。 それでは、メモリ上のこのアドレスにどのような値が入 っているのかを調べてみましよう。ある範囲のメモリの値 を表示するには x コマンド (examine の意味 ) を使いま す。 実行するときは、 x の後ろに、、 / " ( スラッシュ ) を入力 し、それに続けて表示するアドレスの個数や表示する値の フォーマットなどを指定することができます。何も指定し UN 工 X MAGAZINE 2004.4 図 21 メモリ上にる呆された或 ( 1 ) ない場合は、 ・回数 : 1 ・フォーマット : x ( 16 進数 ) 変数 p として 確保された領域 変数 buf として 確保された領域 アドレス 0xbfffe6bc 0xbfffe6c0 ・メモリサイズの単位 : w ( ワード 4 バイト ) となります。 こでは表示させるアドレスの範囲を、 p の領域 0xbfff 45

2. UNIX MAGAZINE 2004年4月号

デバッガの基礎知識 図 24 メモリ内容窈忍 ( 2 ) (gdb) x / 12 &p 0xbfffe6bc : 0xbfffe6cc: Oxbfffe6dc : (gdb) 0X080483ae 0X42015574 9X0043424 長 0X42130a14 0X00000001 0X4000C80 三 0X40015360 0xbfffe724 特集 0xbfffe72c 0xbfffe6f8 0xbfffe6d8 図 25 メモリ内容忍 ( 3 ) (gdb) x/12b &buf [ 0 ] 0xbfffe6c0: 0xbfffe6c8: (gdb) : 0X41 三 0xd8 9X42 0xe6 三 0X43 : 0xff : 0X00 三 0xbf 三 0X60 : 0xc6 0X00 0X40 図 26 メモリ上に石呆された令或 ( 3 ) 変数 p として 確保された領域 変数 buf として 確保された領域 意味のない値 0X41 0X42 0X43 0X00 意味のない値 アドレス 0xbfffe6bc 0xbfffe6c0 残っています ( 図 26 ) 。もちろん、この値には未はあり ません。変数 p にはまだ何も代入されていないので、 p が 確保されているアドレス 0xbfffe6bc の値に変化はありま せん。 それでは、ふたたびもう 1 行実行して、同じメモリ領域 の内容を確認してみます ( 図 27 ) 。さきほど、メモリサイ ズの単位としてバイト (b) を指定したので、これ以降のメ モリサイズの指定はデフォルトでバイトと解釈されるよう UN 工 X MAGAZ 工 NE 2004.4 が設定されたのが分かります。実際には、、 p = buf" とい すると、今度は変数 p の領域 ( 実線で囲った部分ロ値 リサイズの単位としてワード (w) を指定します。 になります。これをワード単位での表示に戻すには、 メモ う文を実行したのですから、 p という変数が buf のアドレ 47 デバッガは、もうすこし複雑なプログラムがどのように プログラムやライブラリの挙動を調べる たでしようか。 どのような値が設定されているのかを実感することができ どういう末をもっているのか、ポインタという変数には こまでの説明で、プログラムで変数を宣言することが たのかカ寉認できます。 これで、それぞれの変数がどのような型として宣言され (gdb) type = char * (gdb) ptype p type char [ 5 ] (gdb) ptype buf ように表示されます。 よび p に対して ptype コマンドを実行してみると、次の かを調べるには ptype コマンドを使います。変数 buf お 変数の値を調べるのではなく、変数がどのような型なの 列 ABC が入っていることカ認できます。 り、その内容は文字列 ABC を指していて、 buf には文字 ポインタ p には buf のアドレス 0xbfffe6cO が入ってお (gdb) $ 4 = "ABC\OOO' " (gdb) p buf $ 3 = 0xbfffe6c0 "ABC" (gdb) p p この時点での、変数 buf と p の値も表示してみましよう。 ( 図 28 ) 。 の先頭アドレス 0xbfffe6c0 が入ったことか確認できます スを指すことになったわけです。事実、 p の領域には buf

3. UNIX MAGAZINE 2004年4月号

特集 デバッガの基礎知識 図 22 メモリ内容Øi 忍 ( 1 ) (gdb) x / 12 &p 確保された領域 変数 buf として 確保された領域 変数 p として (gdb) 0xbfffe6dc : 0xbfffe6cc : 0xbfffe6bc : 図 23 メモリ上に呆された令或 ( 2 ) 0X42015574 0X080483ae : 0X42130a14 ! 0X42130a14 0X00000001 アドレス 0xbfffe6bc 0X4000C6 お 0 三 0X40015360 0xbfffe724 0xbfffe6d8 0xbfffe6f8 0xbfffe72c 意味のない値 0xbfffe6c0 意味のない値 46 ( 省略形は n) コマンドを使います。どちらもプログラムを 実行する ( ステッフ。実行 ) には step ( 省略形は s) や next 定するところまで実行してみます。プログラムを 1 行すっ もうすこしプログラムを進めて、変数 buf に文字列を設 は末はありません ( 図 23 ) 。 こでみているメモリ内容の値に 確保されただけなので、 域です。この時点では、まだローカルな変数として場所が 域、破線で囲った部分カ畯数 buf として確保されている領 図の実線で囲った部分か変数 p として確保されている領 入力しにくいのでこの例では変数名を利用しています。 のようにアドレスの直を直接指定することもできますが、 x コマンドのアドレスを指定する引数には、 0xbfffe6bc う ( 図 22 ) 。 示できればよいのですから、適当に 12 回としてみましょ 数えてももちろんかまいませんが、大雑把にこの範囲カ畩 5 バイト ( buf のサイズ ) ) までにします。回数はちゃんと e6bc から Oxbfffe6c4 (buf の先頭アドレス 0xbfffe6c0 十 1 行すっ実行していくコマンドですが、ほかの関数を呼び 出しているときの動作が異なります。ソースコードが手許 にあるほかの関数を呼び出している場合、 step コマンドで は、その関数の内部に実行が移動します。一方、 next コ マンドでは、その関数は実行されますが内部では実行は停 止せず、関数を呼び出している関数の次の行で止まります。 ほかの関数を呼び出している場合も、ソースコードのない 関数 ( システムのライプラリ関数など ) の場合は、 step コ マンドを使っても関数内部で実行は停止しません。 図 13 の sample3. c では、ソースコードがある関数の 呼出しはおこなわれていないので、どちらのコマンドを使 っても動作は同じです。ここでは step コマンドを実行し、 sprintf 文の行 ( 8 行目 ) が実行されたところまで進みます。 (gdb) s 9 (gdb) p = buf ; sprintf の行が実行されたので、 buf の領域には、、 ABC" という文字列が格納されているはずです。さきほどと同じ ようにして、メモリの内容を確認してみましよう ( 図 24 ) 。 同じく破線で囲った部分が buf の内容です。 char 型の 配列の値なので、デフォルトのワード ( 4 バイト ) 単位で表 示させると見づらいですね。そこで、今度はバイト単位で 表示してみます ( 図 25 ) 。それには、メモリサイズの単位 として b を使います。ここでは buf の内容力寉認でき ればよいので、指定するアドレスも buf の先頭アドレスに します。 さきほどと同じく、破線で囲った部分が buf の内容です。 今度は、見やすくなったのではないでしようか。文字列が 代入される前と比較すると、、、 0X41 0X42 0X43 0X00 " と いう値が入っているのカ寉認できます。文字列、、 ABC" を 数値で表すと、、 0X41 0X42 0X43 0X00 " となるので、メ モリの値もこのようになります。最後の 0X60 という値は buf[4] に相当するメモリの内容ですが、 buf[4] には何も 代入されなかったため、もともとのメモリの値カそのまま UNIX MAGAZ 工 NE 2004.4

4. UNIX MAGAZINE 2004年4月号

特集 デバッガの基礎知識 行目を見ると、セグメンテーション・フォルトの原因とな りそうな引数は、ポインタ p しかなさそうです。そこで、 この p の内容を確認してみましよう。 (gdb) p p NO symbol 1n current context . (gdb) 最初のサンプル・プログラム samplel. c では、 print コ マンドで変数の内容をみることができましたが、今度は、、 p というシンポルはみあたらない " と表示されています。 のエラーメッセージのⅲ current context" という文に 注意してください。 gdb では、 print のように変数などのデータを調べるコ マンドは、ほとんど現在のスタックフレームについてだけ 動作します。これを理解するには、 C 言語のスコープ ( 有 効範囲 ) を思い出すといいでしよう。ここで確認しようと している p という変数は、 subl 関数のなかで宣言されて いるローカル変数なので、ほかの関数からは参照できませ ん。現在、このプログラムは -l()-str-overflow_internal という関数で止まっています。したがって、この関数から は p という subl 関数内のローカル変数は参照できないわ けです。スコープの考え方と同様、グローバル変数はどの スタックフレームにいるときでもつねに調べることができ ます。 別のスタックフレームにあるデータを調査したい場合 は、そのスタックフレームを選択状態にする必要がありま す。上記の gdb のエラーメッセージは、、、 p というシンボ ルは、現在選ばれているスタックフレーム ( コンテキスト ) には存在しないため、表示できない " という未です。 いま、自分がどのスタックフレームを選んでいるのかは frame ( 省略形は f) コマンドで知ることができます。 (gdb) f # 0 0X42070fa4 in _IO_str_overf10w—interna1=> ( ) from /lib/tls/libc . so . 6 プログラムが止まっている場合は、自動的にプログラム が中断したときのスタックフレームが選択状態になります。 現時点では、このプログラムで定義されているグロー バル変数があれば、そのグローバル変数と、 ()g オプシ ョン付きでコンパイルされていれば ) その関数内のローカ ル変数を調べることができます。ここでは関数 -IO-str- overflow-internal は—g 付きでコンノヾイルされていない UNIX MAGAZINE 2004.4 ので、この関数内のローカル変数は調べられません。 subl 関数内のローカル変数 p を調べるには、 subl 関 数を実行しているときのスタックフレームを選択状態にす る必要があります。 選択しているスタックフレームを変更するには frame コ マンドか、 up/down コマンドを使います。 frame コマ ンドでは、引数として移動先のスタックフレームの番号を 指定します。一方、 up/down は 1 つ上 / 下のスタックフ レームに移動します。、、上 / 下のスタックフレーム " とい うのは、 backtrace コマンドで表示されるスタックトレー スに含まれるスタックフレームの川印茅のことです。 いまは一番内側のスタックフレームにいるので、 up コマ ンドで 1 つ上のスタックに移動してみましよう。 (gdb) up # 1 0X4206fb28 in _IO_defau1t_xsputn—interna1=> ( ) from /lib/tls/libc . so . 6 (gdb) ご覧のように、 1 つ上にあたる # 1 のスタックに移動し ました ーで選択したいスタックフレームは、 sprintf を 呼び出している # 5 のスタックフレームです。 up コマンド を何回か実行して # 5 まで移動してもかまいませんが、 こでは frame コマンドで一気に # 5 まで移動します。以 下のように、 frame コマンドの引数に、、 5 " と指定します。 (gdb) f 5 # 5 0X080483a9 in subl ( ) at samp1e2. c : 18 sprintf (), "%s" 18 ( gdb ) これで目的とするスタックフレームが選択できたので、 このスタックフレームにあるローカルな変数を調査できる ようになりました。さきほとべようとしていた変数 p の 値を確認してみましよう。 (gdb) p p $ 1 = 0X0 (gdb) 今度はぶじに p の値が表示され、ポインタの値が 0 に なっていることが分かります。つまり、このセグメンテ ーション・フォルトの本当の原因は、 -IO-str-overflow- internal 関数ではなく、 sprintf を呼び出すときの引数 にあったわけです。あとは、この引数がなぜ 0 になった のかを調べればよいでしよう。 sprintf に渡された変数 p の値は、実際には sprintf から呼び出された -IO-str- 41

5. UNIX MAGAZINE 2004年4月号

図 4 7 つの変数の値を新しい変数にコピー case ${OSTYPE} in FreeBSD) '$portmap—enable " ] & & rpcbind—enable=" $portmap—enable" ーⅡ '$portmap-program " ] & & rpcbind—program=" $portmap—program' '$portmap-flags " ] & & rpcbind—flags=" $portmap-flags " '$single—mountd—enable" ] & & mountd—enable=" $single—mountd—enable" ーⅡ " $xntpd-enable" ] & & ntpd-enable=" $xntpd—enable " ーⅡ " $xntpd-program" ] & & ntpd-program="$xntpd-program" "$xntpd-flags" ] & & ntpd-flags="$xntpd—flags" e S めのものですこの関数では、スクリプトカ引数を 1 っと map-flags 、 single-mountd-enable 、 xntpd-enable 、 り、引数の先には fast か f 。 , 00 カ咐けられると仮定して xntpd-program 、 xntpd-flags という 7 つの変数の値を 新しい変数にコピーしています ( 図 4 ) 。 います。引数の後ろの部分は、関数への引数を使って表し このコードでは、まず test コマンドを使って変数カ第ス疋 ます。 されているかどうかを確認し、その後、 & & を制御構造の コードでは、まず Usage 出力を表す文字列、コマンド ように使って処理を実現しています。最初の行を例にとる 名、引数の前に fast か force を付けられる点について出力 と、 portmap-enable 変数に空でない値が常内されている します。 ときには & & の後ろの部分を実行し、 rpcbind-enable 変 rc—usage() 数に portmap-enable 変数の値が常内されます。 ech0 -n 1 > & 2 "Usage: $ 0 [fastlforce] ( " この部分は、パラメータ展開をうまく使えば制御構造を まったく利用しなくても実現できます。たとえば、上で説 この echo コマンドでは、—n オプションを指定して改行 明した行は次のように言古杢できます。 しないようにしています。これは、実際の引数を別の echo コマンドで出力するので、行の途中で改行されないように rpcbind—enable=${portmap—enable : ー - $rpcbind—enable} 、 1 > & 2 " としてリダイレクション するためです。さらに、 も指定しています。こちらは、通常の echo コマンドでは ます、等号の右側を見てください。この部分でパラメー 標準出力に出力されてしまうので、標準工ラー出力へ出力 タ展開を使っています。通常は portmap-enable 変数の するためです。 値に展開されますが、この値が設定されていなかったり値 指定できる引数は、関数への引数として渡されます。 カ啌だったときには、パラメータ展開のなかの : ーの右の値 れを出力する際には、どれか 1 つを選択できることを示す カ俐用されます。つまり、等号の右側全体で、 portmap- ため、 、、「 ( パイプ ) で区切って出力する必要があります。 enable 変数に値が設疋されていればその値、設疋されて いなければ rpcbind-enable 変数の値を表すことになりま -sep= elem in $ * ; dO す。この値を rpcbind-enable 変数に代入するので、けっ for echo ーⅡ 1 > & 2 "$—sep$—elem" きよくは portmap-enable 変数に値カ殳定されていたと —sep=" 卩 done きに、その値を rpcbind-enable 変数に代入するのと同じ 結果が得られます。 このため、最初は -sep を空にしておき、 1 つの要素を 出力する際にはかならす、、 $-sep$-elem" と出力されるよ うにします。最初は -sep カ啌なので、たんに -elem を出 力したのと同じですが、それ以降の出力では一 sep がパイプ に置き換えられるため、かならずパイプの後ろに次の単語 が並ぶことになります。 rc usage 関数 rc-usage 関数は、スクリプトの実行中に引数に関するな んらかの問題が発生したときに Usage 出力をおこなうた 98 UNIX MAGAZ 工 NE 2004.4

6. UNIX MAGAZINE 2004年4月号

特集 テパッガの基礎知識 アクセスできないアドレスについて ログラムのなかの静的変数、或変数、 malloc で確保した領域が 自分で書いたソースコードをコンパイルして実行形式を作成し、 データ・セグメントに割り当てられます。この領域にはデータが それを実行するとき、実祭には何か起こっているのでしようか。 置かれているので、読出しだけでなく書込みも可能です。データ・ 実行形式とは、 C 言語 ( やその他の言語 ) で言古杢されたプログラ セグメントは、さらに下記の 3 つの領域に分かれています。 ムを、マシンが解釈できる言葉 ( マシン語 ) に奐したファイルで す。このファイルには、プログラムの命令やデータが含まれてい ・データ : 静的変数やた或変数のうち、初期値カ寸定されている ます。この実行形式を実行するとメモリ上にプログラムカ己置さ データが置かれる。 れますが、その際にプログラムの、命令 " や、データ " の部うゞど 伊リ : static int n = 10 ; のように配置されるのかは決められています。この配置を、メモ この変数 n はデータ領域に割り当てられます。 リマップ " といいます。 たいていの UNIX システムでは、メモリマップは下図のよう ・ BSS : 静的変数やた或変数のうち、初期値カ甘定されていない になっています。 データが置かれる。 例 : static int a; 0X0 この変数 a は BSS 領域に割り当てられます。 ・ヒープ : malloc で確保される変数の領域カり当てられる。 例 : char *p ; p = (char*)ma110c(100) ; この malloc で割り当てられた 100 バイトはヒープ領域に確 保されます。 スタック・セグメントは、スタックを置くための領域です。ス タックにはプログラムの自動変数カリり当てられます。たとえば、 main() int a ; 0xffffffff という関数が定義されていれば、この自動変数 a はスタックに割 り当てられます。 この図からも分かるように、メモリマップはおおまかに 3 つの 関数カ剛乎び出されると、スタックフレームという単位でスタッ 部分 ( テキスト・セグメント、 データ・セグメント、スタック・ ク上に領域カ保され、そこに自動変数や関獅乎出し時の引数の セグメント ) に分けられます。データ・セグメントは、さらにデー 情報、関数カ唳るべきアドレスなどの情報が入れられます。関数 夕、 BSS 、ヒープと呼ばれる 3 つの領域に分けられます。 が終了すると、対応するスタックフレームは削除されます。プロ テキスト・セグメントとは、マシン語の命令が置かれる場所で グラムの実行中は、関数の呼出しによってスタック領域は増えたり す。雹歔この領域は読出しや実行は可能ですが、書込みは禁止 減ったりしています。 されています。したがって、プログラムがこの領域に書込みをお このように、プログラムの実行時には、メモリ上の領域は割り こなおうとするとエラーが発生します。 当てられた領域によって役目が異なっているのです。 データ・セグメントはデータの領域です。 C 言語の場合は、プ ドを実行してみると、 core. 1751 ( 数値はそのときの状況に unlimited よって異なります ) というファイルができています。 ・ tcsh の場合 % 1imit coredumpsize coredumpsize 0 kbyt e s % ls % 1imit coredumpsize unlimited a. out core .1751 samplel . c % limit coredumpsize unlimited coredumpsxze 用語のところでちょっと書いたように、コアファイルの サフィックスの数字 ( この例では 1751 ) は、このプログラ さて、制限を解除してから再度プログラムを実行すると、 ムを実行したときのプロセス番号です。このように、出力 今度はさきほどの例のように、、セグメントエラー (core を されたコアファイルに自動的にプロセス番号を付与してく 出力しました ) " と表示されるはずです。ここで、 ls コマン テキスト・セグメント プ テータ・セグメント スタック・セグメント 33 UNIX MAGAZINE 2004.4

7. UNIX MAGAZINE 2004年4月号

特集醪デバッガの基礎知識 図 31 リスト構造 tlist の概要 base number "cell A ” name 次のセルの next アドレス 1 2 "cell B ” 次のセルの アドレス 3 "cell C ” NULL の値の表示などです。 を示していることになります。次の while 文では、このセ ルの next 変数に値カ殳定されているかどうかを確認して Solaris の dbx や HP-UX の xdb 、 Windows の Vi- sual Studio に付属するデバッガ、 Java の開発環境に含 います。この next は、このセルの次のセルを示している まれる jdb なども、コマンド名が異なっていたり GUI を 変数ですから、この変数に値が入っていれば、すでに次の 利用するといった違いはありますが、上記の基本的な機能 セルも存在していることになります。 next に値が入って はすべて備えています。また、 C 言語用のデバッガの多く いなければ、現在のセルがリストの最後ということになり は、 C 十十言言吾も扱えるようになっています。 ます。 今回紹介したのは、デバッガのごく基本的な機能の一部 next 変数に値が入っていれば、その値を qtr 変数に設 だけです。より大きなプログラムをデバッグするには、あ 定し、 qtr 変数が次のセルを指し示すようにして、また次 る条件のときだけプログラムが停止するようにプレークポ のセルの next 変数を調べます。 next 変数カ第ス疋されてい イントを設疋したり、すでに動いているプロセスに、あと ないセルがみつかったら、そのセルの次のセルを表すアド からデバッガをアタッチ 3 したりしてデバッグをおこなう レスとして、 ptr 変数の値を ptr に設疋しています。 こともあります。また、デバッグ対象の言語にとって便利 こまでに説明したコマンドを使えば、 qtr や ptr の値 な機能が含まれていることもあります。これらの機能を知 を確認しながら実行することができます。どこにプレーク っていれば、デバッグ作業をよりスムーズに進められるで ポイントを設定し、どの変数の値を調べると動作カ認し しよう。しかし、デバッガのすべてのコマンドカ噸に入っ やすいのか、ぜひ自分で試してみてください。 ていなくても、とくに困ることはないと思います。ここで このように、デバッガはプログラミングについて学ぶた 紹介したような基本的な機能だけでも、かなりの作業がお めの道具としても、かなり有効だと思います。ここでとり こなえます。あとは、使う側の工夫しだいです。基本的な あげた例だけでなく、自分で書いたプログラムについても 機能だけではどうにも使いづらいと感じたときに、初めて デバッガで調べてみましよう。そうすれば、知らす知らず もっと便利な機能がないかどうか調べてみるという手順で のうちに、コンピュータのアーキテクチャやプログラミン 十分でしよう。ぜひ、皆さんも自分カ勉強している言語や グに対する理解が深まっていくのではないでしようか。 環境のデバッガを使ってみてください。この記事が、プロ グラミングの学習の手助けとなればさいわいです。 ( なかやしき・きようこ ) おわりに 今回はデバッガの例として gdb をとりあげましたが、 般にデバッガと呼ばれるツールの基本的な機能はどれも似 たようなものです。、、基本的な機能 " とは、プレークポイ ントの設疋やステップ実行、スタックの状態の調査、変数 3 gdb から run コマンドでプログラムを走 j するのではなく、稼の」中のプロ グラムに gdb をあとから接続する方法です。この方法を使うと、寺間に わたって実行中のコマンドなど、終了させることのできないプログラムでも動 作を確認することができます。 53 UNIX MAGAZINE 2004.4

8. UNIX MAGAZINE 2004年4月号

連載 /FreeBSD のブートプロセスをみる 図 2 proc 」 inkup() で作られるリンク構造 tqh 」 ast p rocO p—kseg rps tqh_first tq h 」 ast p_threads tq h_fi rst tqh 」 ast thread0 td—proc td—p ⅱ st next tqh—p rev td—kseg rp NULL ksegrp0 kg—proc kg—kseg rp n ext tqh—p rev td_kgli st tq h 」 ast tq h_first kg_th reads tqh—first kg_kseq tq h_prev next NULL kse0 NULL tqh—prev next ke—kg list ke—p roc NULL だに図 2 のようなリンク関係を作ります。このサプルーチ ンは、 kern/kern-thread. c で疋義されています。 それぞれの構造体がもつデータの意味は FreeBSD 5.1 のスレッドモデルに大きく依拠しているため、この記事で は触れません。詳細を知りたい人は、文献 [ 1 ] を参照して カーネル囃竟変数の読込み ください。 1949 : 1950 : 1951 : 1952 : 1955 : if (envmode = = kern—envp = static— else if (bootinfo. bi—envp) kern_ envp bootinfo. bi—envp + KERNBASE ; init-paraml ( ) ; 1 , 949 ~ 1 , 952 行目では、カーネル環境変数の先頭アド レスを kern-envp に設定しています。 FreeBSD のカーネルは、内部に保持しているパラメー タの値を起動時に変更することができます。この設定変更 カ河能なパラメータは、、カーネル環境変数 " と呼ばれ、起動 162 時に bootinfo 構造体を用いてローダからカーネルに渡さ れます 1 。 現在のカーネル環境変数の内容は、 kenv コマンドで表示 することができますが ( 図 3 ) 、残念ながら、設定できる環 境変数を一覧にした表やドキュメントはどこにもないよう です。パラメータを調整したいユーザーは、自分の責任で カーネルを読めということなのでしよう。しかし、通常は UNIX MAGAZINE 2004.4 す。 1 Linux の場合も、ローダからオプション文写列としてカーネルに渡されま static-env を生成します。つまり、 MYKERNEL. env の ァイル MYKERNEL. env を読み込んで環境変数文字列 config コマンドは envmode の定義を 1 に設定し、フ 一方、カーネル設疋ファイルに次の 1 行を追加すると、 渡された環境変数を参照します。 が設定されます。そして、このときカーネルはローダから 的に作成するファイル env. c で疋義され、通常は 0 ( ゼロ ) 1 , 949 行目の変数 envmode は、 config コマンドが自動 とくに何かを設定する必要はないと思います。

9. UNIX MAGAZINE 2004年4月号

特集 1 , ( gdb ) next 0X0 } {number (gdb) p *base デバッガの基礎知識 name 0X8049760 "cell (gdb) s addCe11 (name=0x80485cc "cell A" , number=l , 、 base=0x0) at samp1e4. c : 52 52 (gdb) ptr = makeCeII(name, number) ; addCeII 関数が実行された結果、変数 base に値カ駁疋 もう 1 行実行して、やはり base 変数の値を確認してみ され、 next は 0 のままであることが分かります。 体の number には 1 が、 name には、、” cell A ” " が設疋 ます。これによると、最初のセルの値として TLIST 構造 ポインタが指している先の値を確認したい場合は * を付け のポインタに設定されたアドレスカ寉認できますが、その されました。 print コマンドに base 変数を指定すると、 ましよう。 (gdb) n 21 base addCeII("ce11 C" 3 , base) ; (gdb) p base $ 4 = (TLIST * ) 0X8049750 (gdb) p *base Breakpoint 1 , main ( ) at samp1e4. c : 17 Starting program : /Programs/Samp1e4/a. out Start it from the beginning? (y or Ⅱ ) y already. The program being debugged has been started=> (gdb) run 関数の内部でステッフ。実行してみます。 もう 1 回プログラムを最初から実行し、今度は addCell 関数の内部を詳しくみてみることにしましよう。 示しているのでしようか。これを調べるために、 addCell のが分かります。この next というポインタの値は、何を ませんが、今度はその内容の next に値カ羸疋されている base 変数の指しているアドレス 0X8049750 は変わり next = 0X8049770 } {number = 1 , name = 0X8049760 "ce11 A" , - $ 5 = 17 (gdb) TLIST* base = NULL; さきほどと同じように、 addCeII コマンドが実行される 行の直前までプログラムを進めます。 (gdb) n 19 (gdb) base = addCe11 ("cell 1 , base); ここで addCeII 関数の内部に制御を移したいので、 step コマンドを実行します。 50 すると、さきほどの next コマンドのときとは異なり、プ ログラムの制御は addCeII 関数の内部に移動し、そこで止 まっています。さらに、 addCell 関数カび出されたとき の引数の情報や、ソースコードの何行目にあたるのかも表 示されています。ここで、 addCell 関数のソースコードを 確認しておきましよう。 ソースコードを表示するコマンドは 1 (list) でした ( 図 30 ) 。 list コマンドはデフォルトでは 10 行しか表示しないの で、関数の全体は確認できません。 gdb のプロンプトカ俵 示されているときに Return キーだけを入力すると、直前 のコマンドにこでは list) が再度実行されます。引数を 付けずに list コマンドを実行すると、ソースコードの続き か表示されるため、図の例では Return キーを入力しただ けでソースコードの残りの部分が表示されています。 これを見ると、 makeCeII 関数を呼び出して、その結果 を ptr に返しています。この ptr は base 変数が NULL のときはそのまま返され、そうでない場合は 60 行目で qtr->next" という変数に代入されています。 この・ -- - ・連の処理、すなわち addCell 関数カ野び出される たびに makeCell 関数カ剛乎び出され、 ptr という値が言定 されるまでの処理を確認してみましよう。ここまでに紹介 したコマンドを使って調べることもできますが、 こでは addCeII コマンド内で実行カ阯まるたびに、自動的に ptr 変数の内容を表示するように設定してみます。 プログラムの実行カ阯まるたびに変数の値を表示させる には display コマンドを使います。ここでは ptr 変数が 指している内容を調べたいので、引数に *Ptr を指定する 必要があります。 (gdb) display *ptr 1 : *Ptr = number = 1108543776 , , name = 0X400160b0 " " 0X4000bCb0 ne Xt この時点では、 ptr 変数は宣言されただけで値は何も設 定されていません。ですから、表示されている値も適当な 値になっています。 UN 工 X MAGAZ 工 NE 2004.4

10. UNIX MAGAZINE 2004年4月号

図 1 スクリプトの実寺に参照される変数のネ騏用ヒ unset name command command_args command_interpreter \ extra—commands pidfile procname \ rcvar required—dirs required—files required—vars eval unset ${-arg}-cmd ${-arg}-precmd ${-arg}-postcmd 図 2 スクリプト・ファイルをサプシェルで実行 # run if [ —x $_file ] ; then if [ ーⅡ 'l$rc_fast_and—loose" ] ; then set $-arg ( trap "echO 'Reboot interrupted' ; exit 1 " 3 $-file ) set $—arg in subshell $-file f i fi e S に正しく動作しないことがあります。そのため、スクリプ run-rc-command 関数の動作に景彡響 トを実行する前に をおよばす変数を初期化しておきます。 処理自体は簡単で、 unset コマンドの引数に初期化した い変数を指定するだけです。 -arg 変数に依存する変数も初 期化しなければならないため、それらについては eval コマ ンドを使って初期化しています。 次は、指定されたスクリプトのファイル名にもとづいて 処理を変える部分です。ますはファイル名が . sh で終って いる場合です。この場合にはサプシェルを起動せず、カレ ントシェルでファイルを実行します。 ・ orig) # scratch 、 file ; skip case " $ in set $-arg run in current she11 $-file カレントシェルでスクリプトを実行するときは、、 . " コマ ンドを使います。 . コマンドは、引数として指定されたフ ァイルの内容力そのまま入力されたかのように動作します。 ただし、この場合にはスクリプトに引数を渡すことができ ません。そこで、 set コマンドを使い、位置パラメータと して引数を渡します。位置パラメータに設疋した状態でス クリプトを呼び出すと、スクリプト内からは引数として指 定されたかのように扱えます。 次はバックアップ・ファイルの場合です。これらのファ イルカ甘旨定された場合には、 warn ( 彳 ) を使って警告メ ッセージを出力します。 96 'lgnorlng scratch file $—file" バックアップ・ファイルとして認識されるのは、末尾が warn UNIX MAGAZINE 2004.4 使って実行します。 ナルの設定をおこないつつ、 set コマンドと . コマンドを での実行になります。この場合には、 trap コマンドでシグ 行します。これらの条件を満たさないときは、サプシェル set コマンドで引数を設疋して . コマンドでスクリプトを実 定されていたら、さきほどの . sh ファイルの場合と同様に には、 rc-fast-and 」 oose 変数の値を確認します。値カ毅 いなければ、何も処理をおこないません。実行可能な場合 うかを確認します。ファイルのモードが実行可能になって まず、指定されたスクリプト・ファイルが実行可能かど プシェルを起動せずに直接カレントシェルで実行します。 ただし、 rc-fast-and 」 oose 変数カ又疋されていたら、サ プト・ファイルとみなし、サプシェルで実行します ( 図 2 ) 。 こまでの条件にマッチしないファイルは通常のスクリ 理しないようにしているわけです。 こでそれらを処 らのファイルも含まれてしまいますか、 を処理したいときに、メタキャラクタの、、 * " を使うとこれ あります。あるディレクトリに置かれたすべてのファイル したときなどに、バックアップのために作成されることが ルです。これらのファイルは、エデイタでファイルを編集 ~ か # のファイルと、拡張子が OLD または orig のファイ