「コマンドの実行 exec」の版間の差分

提供: C言語入門
移動: 案内検索
(ページの作成:「Unixでプログラムを実行するには、exec系システムコールを利用します。exec系システムコールを実行するとプログラムが置き換...」)
 
(exeveで引数を渡す簡単な例)
行131: 行131:
 
#include <stdlib.h>
 
#include <stdlib.h>
 
#include <unistd.h>
 
#include <unistd.h>
 +
#include <err.h>
  
 
int
 
int
行142: 行143:
 
                 NULL
 
                 NULL
 
         };
 
         };
         execv(path, _argv);
+
         int r = execv(path, _argv);
 +
        if (-1 == r) {
 +
                err(EXIT_FAILURE, "can not execv(%s)", path);
 +
        }
 +
        // NOTREACHED
 
         exit(EXIT_SUCCESS);
 
         exit(EXIT_SUCCESS);
 
}
 
}
行156: 行161:
 
kaoru
 
kaoru
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 
== forkしてechoする例 ==
 
== forkしてechoする例 ==
 
=== fork_echo.c ===
 
=== fork_echo.c ===

2014年5月15日 (木) 00:57時点における版

Unixでプログラムを実行するには、exec系システムコールを利用します。exec系システムコールを実行するとプログラムが置き換わります。そのため、元のプログラムに戻ることはありません。

読み方

exec
いくぜっく

概要

exec系システムコール execl, execlp, execle, exect, execv, execvp, execvP は、ファイルを実行します。 関数によって渡せるパラメータが違います。

  • サーチパスから自動的にコマンドを探し、実行する関数
  • 環境変数まで指定できる関数

システム関数

system関数は、プログラムを実行できる関数です。 この実装は、以下のような流れになっています。

  1. fork
    1. 子プロセス
    2. signal 設定
    3. execl (_PATH_BSHELL "sh", "-c", command, NULL)
  2. 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 は、atexit で登録された関数、 signalで登録されたシグナルハンドラが呼び出されません。
  • _Exitは、オープンされているファイルディスクリプタをクローズしない。

実装に依存する処理は以下のものがあります。

  • 標準I/Oのバッファのフラッシュ
  • tmpfileで作成されたテンポラリファイルの削除
     #include <stdlib.h>
 
     void
     exit(int status);
 
     void
     _Exit(int status);
 
     #include <unistd.h>
 
     void
     _exit(int status);

関連項目