コマンド引数と環境変数
Rev.4を表示中。最新版はこちら。
環境変数は子プロセスへ引き継がれていく。すなわち各プロセス間で環境変を管理している。シェルからプロセスを起動すると、最終的にコマンド引数と環境変数を引数に取るdo_execveが呼ばれる。(シェルで環境変数としてexportはシェルが管理していて、シェルプロセスとしてこの変数が環境変数に設定されるわけでない。)そして、引数のコマンド引数および環境変数を、カーネルがアロケートしたstruct linux_binprm構造体内のページに複写し、それをプロセスのスタックエリアに持ってこれるようにリニアアドレスを調整すべきページテーブルを変更し、最後にスタックをその領域分拡張することで、コマンド引数と環境変数を子プロセスのスタック上に継承させているようだ。(たぶん、たぶん、たぶんん・・・・)
したがって、子プロセスはそのスタックからコマンド引数と環境変数を取得することが可能となる。ことの時の設定されたスタックは、スタックトップからコマンド数、コマンド変数1のアドレス・・・コマンド変数nのアドレス、デリミターとしての0、環境変数1のアドレス・・・環境変数nのアドレスとなり、以降に実際のコマンド引数群と環境変数群が設定されていく。
簡単なCのプログラムを-gオプションでコンパイルし、gdb下でまず_startにブレイクしてその時点までrunした後のスタックの内容を確認してみる。
a.c #include "stdio.h" main(int argc, char* argv[]) { printf("%x\n", &argc); }
[root@localhost kitamura]# gcc a.c -g [root@localhost kitamura]# gdb a.out (gdb) set arg aaa bbb <- コマンド引数 aaa,bbbをセット (gdb) b _start <- _startにブレイクポイント (gdb) b main <- main()にブレイクポイント (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/20 $esp <- _startのスタックargc,argv[0],argv[1],argv[2],9,ebv[0]... 0xbffff7d0: 0x00000003 0xbffff8fc 0xbffff911 0xbffff915 0xbffff7e0: 0x00000000 0xbffff919 0xbffff938 0xbffff948 0xbffff7f0: 0xbffff953 0xbffff961 0xbffff982 0xbffff998 0xbffff800: 0xbffff9b6 0xbffff9c9 0xbffff9d3 0xbffffe14 0xbffff810: 0xbffffe1f 0xbffffea4 0xbffffebe 0xbffffecd (gdb) x/40s 0xbffff8fc <- argv[0]以降の内容 0xbffff8fc: "/home/kitamura/a.out" 0xbffff911: "aaa" 0xbffff915: "bbb" 0xbffff919: "HOSTNAME=localhost.localdomain" 0xbffff938: "SHELL=/bin/bash" : 0xbffffecd: "PWD=/home/kitamura" 0xbffffee0: "INPUTRC=/etc/inputrc" 0xbffffef5: "LANG=ja_JP.eucJP" :
Cのmain関数はglibがこのスタックから必要な変数を再度スタックに積んでmainをコールすることになる。
(gdb) c <- コンティニュー Continuing. Breakpoint 2, main (argc=3, argv=0xbffff7d4) at a.c:5 5 printf("%x\n", &argc); (gdb) <- ステップ実行でargcのアドレスの確認 bffff750 6 }
(gdb) x/60x $esp <- mainコール時のスタック内容 0xbffff720: 0x080484b4 0xbffff750 0xbffff748 0x08048419 0xbffff730: 0x00ae06d0 0xbffff750 0xbffff7a8 0x00b0b6e5 0xbffff740: 0x08048400 0x08048310 0xbffff7a8 0x00b0b6e5 0xbffff750: 0x00000003 0xbffff7d4 0xbffff7e4 0xb7ff02d8 <- main(argc, argv[]) 0xbffff760: 0x00000001 0x00000001 0x00000000 0x0804822c 0xbffff770: 0x00c64ff4 0x08048400 0x08048310 0xbffff7a8 0xbffff780: 0x742bf9b5 0xeaa80ccb 0x00000000 0x00000000 0xbffff790: 0x00000000 0x00ae6530 0x00b0b60d 0x00af1fc0 0xbffff7a0: 0x00000003 0x08048310 0x00000000 0x08048331 0xbffff7b0: 0x080483c4 0x00000003 0xbffff7d4 0x08048400 0xbffff7c0: 0x080483f0 0x00ae06d0 0xbffff7cc 0x00000000 0xbffff7d0: 0x00000003 0xbffff8fc 0xbffff911 0xbffff915 <- カーネルがセット 0xbffff7e0: 0x00000000 0xbffff919 0xbffff938 0xbffff948 0xbffff7f0: 0xbffff953 0xbffff961 0xbffff982 0xbffff998 0xbffff800: 0xbffff9b6 0xbffff9c9 0xbffff9d3 0xbffffe14