int execve(const char *filename, char *const argv[],
char *const envp[]);
#! interpreter [optional-arg]
後者の詳細は、後ろの「インタプリタ・スクリプト」の節を参照のこと。
argv は新しいプログラムに渡される引き数文字列の配列である。 envp は文字列の配列であり、伝統的に key=value の形式を しており、新しいプログラムの環境変数として渡される。 argv と envp はいずれもの NULL ポインタで終わっている 必要がある。引き数配列と環境変数は、呼び出されたプログラムの main 関数を 以下のように定義することによってアクセス可能になる。
int main(int argc, char *argv[], char *envp[])
成功した場合、 execve() は返らない。 そして、呼び出し元のプロセスの text, data, bss, スタックは、 読み込まれたプログラムによって上書きされる。
元のプログラムが ptrace されている場合、 execve() が成功した後に そのプログラムに SIGTRAP が送られる。
filename で指定されたプログラムファイルに set-user-ID ビットが設定されており、 ファイルが存在するファイルシステムが nosuid (mount(2) の MS_NOSUID フラグ) でマウントされておらず、 呼び出したプロセスが ptrace されていない場合、 呼び出したプロセスの実効 (effective) ユーザ ID は プログラムファイルの所有者 (owner) に変更される。 同様に、プログラムファイルに set-group-ID ビットが設定されていた場合、 呼び出したプロセスの有効グループ ID は プログラムファイルのグループに変更される。
プロセスの実効ユーザ ID は保存 (saved) set-user-ID にコピーされる。 同様に、実効グループ ID は保存 set-group-ID にコピーされる。 このコピーは、set-user-ID / set-group-ID 許可ビットにより発生する 実効 ID の変更後に行われる。
実行ファイルが動的リンクされた a.out 実行形式で、共有ライブラリの スタブを含むものだった場合、実行の開始時に Linux の ダイナミック・リンカ ld.so(8) が呼び出され、必要な共有ライブラリをメモリに読み込んでリンクを行う。
実行ファイルがダイナミック・リンクされた ELF 実行形式だった場合、 PT_INTERP セグメントに指定されたインタプリタが必要な 共有ライブラリ (shared library) を読み込むのに使用される。 通常、インタプリタとしては、 Linux libc 5 をリンクしたバイナリの場合には /lib/ld-linux.so.1 が、 glibc 2 をリンクしたバイナリの場合には /lib/ld-linux.so.2 が使用される。
以下に示す以外のすべてのプロセス属性は execve() の前後で保持される。
上記のリストのプロセス属性はいずれも POSIX.1-2001 で規定されている。 以下に示す Linux 固有のプロセス属性も execve() の前後で保持されない。
以下の点についても注意すること:
#! interpreter [optional-arg]
interpreter は有効な実行ファイルのパス名でなければならず、 それ自身がスクリプトであってはならない。 execve() の filename 引き数がインタプリタスクリプトを指定している場合、 interpreter は以下の引き数で起動される。
interpreter [optional-arg] filename arg...
arg... は execve() の argv 引き数が指すワード列である。
移植性を持たすには、 optional-arg は空か 1ワードだけにすべきである (つまり、ホワイト・スペースを含めるべきではない)。 下記の「注意」の節を参照。
カーネル 2.6.23 より前の Linux では、環境変数と引き数の文字列群を 格納するのに使用されるメモリは 32 ページに制限されていた (32 ページというのはカーネル定数 MAX_ARG_PAGES で定義される)。したがって、 ページサイズが 4 kB のアーキテクチャでは、 最大サイズは 128 kB ということになる。
カーネル 2.6.23 以降では、ほとんどのアーキテクチャで リソースのソフト上限 RLIMIT_STACK に基づいたサイズ上限が使われる。 これらのアーキテクチャでは、合計サイズの上限は許可されたスタックサイズの 1/4、各文字列の上限は 32 ページ (カーネル定数 MAX_ARG_STRLEN) で、文字列数の最大値は 0x7FFFFFFF である。 (この変更により、プログラムはこれまでより大きな引き数や環境変数の リストを取れるになる。 合計サイズにスタックサイズの 1/4 の上限を設けているのは、 新しいプログラムが必ずある程度のスタック空間を持てることを保証する ためである。) メモリ管理ユニット (MMU) を持たないアーキテクチャは上記の変更の 例外である。これらのアーキテクチャではカーネル 2.6.23 より前と 同じ上限がそのまま使用される。
Linux はスクリプトの set-user-ID と set-group-ID ビットを無視する。
ファイルシステムを nosuid でマウントした場合に set-user-ID/set-group-ID の実行ファイルを どの様に扱うかは、Linux カーネルのバージョンによって異なる: あるバージョンでは、すでに必要な権限を持っている場合を除いて、 その実行を拒否する (そして EPERM を返す)。別のあるバージョンでは set-user-ID/set-group-ID ビットのみを無視し exec() は成功する。
#! 実行形式のシェル・スクリプトの 1行目に許されている文字数は、 最大 127 文字である。
インタプリタ・スクリプトの optional-arg 引き数の解釈方法は実装により異なる。 Linux では、インタプリタ名 interpreter に続く文字列全体がインタプリタに 1個の引き数として渡される。 しかし、動作が異なるシステムもある。 あるシステムでは、 optional-arg のうち最初のホワイト・スペースまでが 引き数として渡される。 また、別のシステムでは インタプリタ・スクリプトは複数の引き数を持つことができ、 optional-arg 内のホワイト・スペースが引き数の区切りとなる。
Linux では、 argv や envp に NULL を指定することができる。これは、これらの引き数に NULL ポインタ 1個だけを含むリストへのポインタを指定したのと同じ効果を持つ。 「この間違った機能を利用しないこと」。 これは非標準で、移植性もない。 他のほとんどの Unix システムでは、これを行うとエラーになる。
/* myecho.c */ #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int j; for (j = 0; j < argc; j++) printf("argv[%d]: %s\n", j, argv[j]); exit(EXIT_SUCCESS); }
以下のプログラムは、コマンドライン引き数で指定した名前のプログラムを 実行するのに使う。
/* execve.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> int main(int argc, char *argv[]) { char *newargv[] = { NULL, "hello", "world", NULL }; char *newenviron[] = { NULL }; assert(argc == 2); /* argv[1] identifies program to exec */ newargv[0] = argv[1]; execve(argv[1], newargv, newenviron); perror("execve"); /* execve() only returns on error */ exit(EXIT_FAILURE); }
二つ目のプログラムを使って一つ目のプログラムを実行するには 以下のようにする。
$ cc myecho.c -o myecho $ cc execve.c -o execve $ ./execve ./myecho argv[0]: ./myecho argv[1]: hello argv[2]: world
さらに、これらのプログラムを使って、スクリプト・インタプリタの例を示す。 このために、「インタプリタ」として先ほど作成したプログラム myecho を使うスクリプトを作成する。
$ cat > script.sh #! ./myecho script-arg ^D $ chmod +x script.sh
作成しておいたプログラムを使ってスクリプトを実行する。
$ ./execve ./script.sh argv[0]: ./myecho argv[1]: script-arg argv[2]: ./script.sh argv[3]: hello argv[4]: world