コマンドの実行 exec
提供: C言語入門
スポンサーリンク
Unixでプログラムを実行するには、exec系システムコールを利用します。exec系システムコールを実行するとプログラムが置き換わります。そのため、元のプログラムに戻ることはありません。
読み方
- exec
- いくぜっく
目次
概要
exec系システムコール execl, execlp, execle, exect, execv, execvp, execvP は、ファイルを実行します。 関数によって渡せるパラメータが違います。
- サーチパスから自動的にコマンドを探し、実行する関数
- 環境変数まで指定できる関数
システム関数
system関数は、プログラムを実行できる関数です。 この実装は、以下のような流れになっています。
- fork
- 子プロセス
- signal 設定
- execl (_PATH_BSHELL "sh", "-c", command, NULL)
- wait4
FreeBSD の libc の system.c の実装の抜粋です。
int __system(const char *command) { pid_t pid, savedpid; int pstat; struct sigaction ign, intact, quitact; sigset_t newsigblock, oldsigblock; if (!command) /* just checking... */ return(1); /* * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save * existing signal dispositions. */ ign.sa_handler = SIG_IGN; (void)sigemptyset(&ign.sa_mask); ign.sa_flags = 0; (void)_sigaction(SIGINT, &ign, &intact); (void)_sigaction(SIGQUIT, &ign, &quitact); (void)sigemptyset(&newsigblock); (void)sigaddset(&newsigblock, SIGCHLD); (void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); switch(pid = fork()) { case -1: /* error */ break; case 0: /* child */ /* * Restore original signal dispositions and exec the command. */ (void)_sigaction(SIGINT, &intact, NULL); (void)_sigaction(SIGQUIT, &quitact, NULL); (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); _exit(127); default: /* parent */ savedpid = pid; do { pid = _wait4(savedpid, &pstat, 0, (struct rusage *)0); } while (pid == -1 && errno == EINTR); break; } (void)_sigaction(SIGINT, &intact, NULL); (void)_sigaction(SIGQUIT, &quitact, NULL); (void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL); return(pid == -1 ? -1 : pstat); }
ヘッダファイル
#include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ..., /*, (char *)0, */); int execlp(const char *file, const char *arg, ..., /*, (char *)0, */); int execle(const char *path, const char *arg, ..., /*, (char *)0, char *const envp[], */); int exect(const char *path, char *const argv[], char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvP(const char *file, const char *search_path, char *const argv[]);
execvの簡単な例
execv1.c
#include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { char *path = "/usr/bin/whoami"; char *_argv[] = { "whoami", NULL }; execv(path, _argv); exit(EXIT_SUCCESS); }
コンパイル
$ cc execv1.c
実行例
ここでは、あなたのユーザ名が表示されます。
$ ./a.out kaoru
exeveで引数を渡す簡単な例
hello.c
#include <stdlib.h> #include <unistd.h> #include <err.h> int main(int argc, char *argv[]) { char *path = "/bin/echo"; char *_argv[] = { "echo", "hello", "world", NULL }; int r = execv(path, _argv); if (-1 == r) { err(EXIT_FAILURE, "can not execv(%s)", path); } // NOTREACHED exit(EXIT_SUCCESS); }
コンパイル
$ cc execv1.c
実行例
ここでは、あなたのユーザ名が表示されます。
$ ./a.out kaoru
forkしてechoする例
fork_echo.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <err.h> #include <sys/types.h> #include <sys/wait.h> int main(int argc, char *argv[]) { pid_t pid = fork(); if (-1 == pid) { err(EXIT_FAILURE, "can not fork"); } else if (0 == pid ) { char *path = "/bin/echo"; char *_argv[] = { "echo", "hello", "world", NULL }; execv(path, _argv); _Exit(EXIT_FAILURE); // NOTREACHED } int status; pid_t wpid = waitpid(pid, &status, WCONTINUED); if (-1 == wpid) { err(EXIT_FAILURE, "waitpid error"); } (void) printf ("status=%d\n", WEXITSTATUS(status)); exit(EXIT_SUCCESS); }
コンパイル
$ cc fork_echo.c
実行例
$ ./a.out hello world status=0
exitと_Exit
- _Exit と _exit は等価です。
- exit は、 ANSI C で規定されています。
- _exit は、POSIX.1 で規定されています。
exit()の動作は、以下の通りです。
- exit()は、標準入出力ライブラリの後始末を常に行います。
- 開いているすべてのストリームに対してfclose()を呼びます。
- バッファされたデータは、フラッシュされます(ファイルへ書き出されます)。
_Exit の特徴は、以下の通りです。
- _Exit は、atexit で登録された関数、 signalで登録されたシグナルハンドラが呼び出されません。
- 出力がフラッシュされません。
実装に依存する処理は以下のものがあります。
- 標準I/Oのバッファのフラッシュ
- tmpfileで作成されたテンポラリファイルの削除
fork()したときにバッファのデータも親プロセスから子プロセスにコピーされます。これだけが理由ではありませんが、同じデータがflushされることを回避するために、子プロセスでは、_exit()で終了するのがお作法です。
#include <stdlib.h> void exit(int status); void _Exit(int status); #include <unistd.h> void _exit(int status);
FreeBSDの環境では、 hoge は、表示されずにプログラムが終了されます。
#include <stdlib.h> #include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { printf("hoge"); _exit(EXIT_SUCCESS); }
関連項目
- プロセスの作成 fork
- 複数の子プロセスをforkする
- コマンドの実行 exec
- popenでコマンドの出力を読み込む
ツイート
スポンサーリンク