関数仕様
書式
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <unistd.h> /* システムコール */ int execve(const char* filepath, char *const argv[], char *const envp[]); /* 標準Cライブラリ関数 */ int execl(const char *filepath, const char *arg, ...); int execlp(const char *filepath, const char *arg, ...); int execle(const char *filepath, const char *arg, ...); int exect(const char *filepath, char *const argv[], char *const envp[]); int execv(const char *filepath, char *const argv[]); |
引数
filepath | プログラム(実行バイナリ or スクリプト)ファイルのパス |
argv | プログラムに渡す引数の配列(NULLポインタで終端) |
envp | プログラムに渡す環境変数の配列(NULLポインタで終端) |
戻り値
なし | 成功の場合はリターンしない |
-1 | execve に失敗 |
機能
- 引数 filepath で指定したプログラムを実行する
- プログラムの起動引数は argv、環境変数は envp で指定可能
- execve を呼び出した元のプログラムは、指定したプログラムに置き換わって実行される
注意すること
注意ポイント①
環境変数 PATH は参照されない
execve では、環境変数の PATH は参照されません。
そのため、引数の filepath には、実行したいプログラムの絶対パス、
またはカレントディレクトリからの相対パスを指定します。
また、filepath の指定とは別に、プログラムに渡す第0引数である argv[0] も別途指定します。
注意ポイント②
execve で実行したプログラムのプロセスIDは、呼び出し前と同じIDのまま変わらない
execve で実行したプログラムのプロセスIDは、
execve を呼び出したプログラムと同じプロセスIDのまま変わりません。
通常、execve を呼び出すプログラムは、まず fork で子プロセスを作成した後、
その子プロセスから execve を呼び出して目的のプログラムを実行します。
あらかじめ fork を実行することにより、目的のプログラムを実行した後も
元のプログラム自身の実行を継続することができます。
関連
子プロセスを生成する fork については、「fork関数の使い方と注意点」にまとめています。
execve 関連の標準Cライブラリ関数
冒頭の書式で少し紹介しましたが、標準Cライブラリ関数には、
execve システムコールを呼び出すラッパー関数があります。
execl() のように語尾に「l」が付く関数は、プログラムの引数を関数の引数に直接指定します。
execlp() のように語尾に「p」が付く関数は、環境変数 PATH を参照してプログラムを検索します。
execl() のように語尾に「e」が付かない関数は、呼び出し元の環境変数をそのまま継承します。
関数名 | 種別 | 起動引数 | PATHの参照 | 環境変数 |
execl() | 標準Cライブラリ関数 | 直接記述 | しない | 継承 |
execlp() | 標準Cライブラリ関数 | 直接記述 | する | 継承 |
execle() | 標準Cライブラリ関数 | 直接記述 | しない | 引数指定 |
execv() | 標準Cライブラリ関数 | 配列渡し | しない | 継承 |
execvp() | 標準Cライブラリ関数 | 配列渡し | する | 継承 |
execve() | システムコール | 配列渡し | しない | 引数指定 |
サンプルプログラム
システムコール「execve」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#include <sys/types.h> #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> static void mask_sigchld (void) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); pthread_sigmask(SIG_BLOCK, &sigset, NULL); } static void unmask_sigchld (void) { sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); } int main(int argc, char *argv[]) { pid_t pid; char *argv[3]; extern char **environ; /* 複数子プロセスの終了を待つ場合など、 fork() 処理中は SIGCHLD をブロックする*/ mask_sigchld(); pid = fork(); if (pid > 0) { /* 親プロセス */ write(1, "parent process\n", 15); } else if (pid == 0) { /* 子プロセス */ write(1, "child process\n", 14); /* 意図しない fd の複製によるバグを防ぐ */ /* 0=stdin, 1=stdout, 2=stderr */ /* 1024= ulimit -n の結果 */ int i; for(i = 3; i < 1024; i++) { close(i); } argv[0] = "echo"; argv[1] = "Hello World!"; argv[2] = NULL; execve("/bin/echo", argv, environ); perror("execve"); /* ここに到達した場合はエラー */ _exit(0); } else { /* エラー */ perror("fork"); } unmask_sigchld(); return 0; } |
標準Cライブラリ関数
1 2 3 4 5 6 |
execl("/bin/echo", "echo", "Hello World!", NULL); execlp("echo", "echo", "Hello World!", NULL); execle("/bin/echo", "echo", "Hello World!", NULL, environ); execv("/bin/echo", argv); execvp("echo", argv); |