コマンド引数と環境変数
Rev.1を表示中。最新版はこちら。
環境変数は子プロセスへ引き継がれていく。すなわち各プロセス間で環境変を管理している。シェルからプロセスを起動すると、最終的にコマンド引数と環境変数を引数に取るdo_execveが呼ばれる。(シェルで環境変数としてexportはシェルが管理していて、シェルプロセスとしてこの変数が環境変数に設定されるわけでない。)そして、引数のコマンド引数および環境変数を、カーネルがアロケートしたstruct linux_binprm構造体内のページに複写し、それをプロセスのスタックエリアに持ってこれるようにリニアアドレスを調整すべきページテーブルを変更し、最後にスタックをその領域分拡張することで、コマンド引数と環境変数を子プロセスに継承させているようだ。(たぶん、たぶん、たぶんん・・・・)
したがって、子プロセスはそのスタックからコマンド引数と環境変数を取得することが可能となる。ことの時の設定されたスタックは、スタックトップからコマンド数、コマンド変数1のアドレス・・・コマンド変数nのアドレス、デリミターとしての0、環境変数1のアドレス・・・環境変数nのアドレスとなり、以降に実際のコマンド引数群と環境変数群が設定されていく。
簡単なCのプログラムを-gオプションでコンパイルし、gdb下でまず_startにブレイクしてその時点までrunした後のスタックの内容を確認してみる。
a.c #include "stdio.h" main() { }
[root@localhost kitamura]# gcc a.c -g [root@localhost kitamura]# gdb a.out (gdb) set arg aaa bbb <- コマンド引数としてaaa,bbbをセット (gdb) b _start Breakpoint 1 at 0x80482e0 (gdb) r Starting program: /home/kitamura/a.out aaa bbb
スタックのトップからの内容である。まず3はargcで、続いてargv[0],argv[1],argv[2]その次がデリミタターとしての0で、以降環境変数のポインタが続いている。
(gdb) x/20 $esp 0xbffff7d0: 0x00000003 0xbffff8fd 0xbffff912 0xbffff916 0xbffff7e0: 0x00000000 0xbffff91a 0xbffff939 0xbffff949 0xbffff7f0: 0xbffff954 0xbffff962 0xbffff982 0xbffff998 0xbffff800: 0xbffff9b6 0xbffff9c9 0xbffff9d3 0xbffffe14 0xbffff810: 0xbffffe20 0xbffffea5 0xbffffebf 0xbffffece
argv[0]以降をダンプしてみると、上のアドレスに従ってコマンド変数と環境変数が続いている。
(gdb) x/50s 0xbffff8fd 0xbffff8fd: "/home/kitamura/a.out" 0xbffff912: "aaa" 0xbffff916: "bbb" 0xbffff91a: "HOSTNAME=localhost.localdomain" 0xbffff939: "SHELL=/bin/bash" 0xbffff949: "TERM=xterm" 0xbffff954: "HISTSIZE=1000" 0xbffff962: "SSH_CLIENT=192.168.95.1 7660 22" 0xbffff982: "QTDIR=/usr/lib/qt-3.3" 0xbffff998: "QTINC=/usr/lib/qt-3.3/include" 0xbffff9b6: "SSH_TTY=/dev/pts/0" 0xbffff9c9: "USER=root" : : 0xbffffea5: "MAIL=/var/spool/mail/root" 0xbffffebf: "_=/usr/bin/gdb" 0xbffffece: "PWD=/home/kitamura"Cのmain関数はglibがこのスタックから必要な変数を再度スタックに積んでmainをコールすることになる。