日々精進

aikoと旅行とプログラミング

指定したプログラムを実行するexec関数メモ

今回は引数によって指定したプログラムを実行するexec関数についてのメモ。今回はexecl関数とexecv関数について書く。

書式

#include <unistd.h>

int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execv(const char *path, char *const argv[]);

概要

exec系関数は、現在のプロセスを指定したプログラムに置き換えて実行する。そのため呼び出し元へ制御が戻ることはない。
関数に渡される第1引数は実行されるプログラムのパスである。それ以降はいわゆるコマンドライン引数を渡す形となる。第2引数に関してexeclは可変長の引数、execvは引数として配列が渡されることになる。また、引数リストの最後はNULLでなければならず、(char *)NULLとすべきである。

返り値

exec関数はエラーが起こった場合のみ呼び出し元に-1を返す。それ以外の場合は復帰せず返り値も存在しない。

実装例

execlを用いてechoを利用する例
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main(){
  errno = 0;
  execl("/bin/echo", "/bin/echo", "hoge", "fuga", NULL);
  
  if(errno != 0)
    perror(strerror(errno));

  return -1;
}

出力結果は以下のとおり

$ a.out
foo bar
execvを用いてechoを利用する例
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(int argc, char *argv[]){
  char *const str[] = {"/bin/echo", "foo", "bar", NULL};

  execv("/bin/echo", str);
  
  if(errno != 0)
    perror(strerror(errno));

  return -1;
}

出力結果は以下のとおり

$ a.out
foo bar
execl関数を用いて任意のプログラムを実行
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(){
  errno = 0;
  
  execl("/foo/bar/hello", "/foo/bar/hello", NULL) ;

  if(errno != 0)
    perror(strerror(errno));

  return -1;
}

今回実行するプログラムはHelloと出力するだけの簡単なプログラム。

#include <stdio.h>

int main(){
  printf("Hello\n");
  return 0;
}

出力結果は以下のとおり

$ a.out
Hello
execl関数がエラーとなる場合
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main(){
  errno = 0;
  // 第一引数にtypo有
  execl("/bin/ech", "/bin/echo", "hoge", "fuga", NULL);
  
  if(errno != 0)
    perror(strerror(errno));

  return -1;
}

以下の様なメッセージが出力される。

$ a.out
No such file or directory: No such file or directory

今まで使ったことない関数もあったのでそれについても併記する。

perror関数
#include <stdio.h>

void perror(const char *s);

システムエラーメッセージを出力する関数。

strerror関数
#include <string.h>

char *strerror(int errnum);

引数に渡されたエラーコードに対応した文字列の先頭ポインタを返す。大域変数errnoにエラーコードが格納されてるので、その値を使って呼び出すと良い。

forkと組み合わせて使う

前途の通り、execを呼び、かつ元の処理を実行させたい時にはfork関数を活用すると良い。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
  int pid;
  int code;
  int status;
  pid_t result;
  int i = 0;

  while(i < 10){
    pid = fork();
    
    // fork失敗
    if(pid == -1){
      fprintf(stderr, "Error\n\n");
    }
    
    // 子プロセスの処理
    if(pid == 0){
      execl("/Users/keisuke/program/c/OS1/hello", "/Users/keisuke/program/c/OS1/hello", NULL);
    }else{
      result = wait(&status);
      
      if(result < 0){
    fprintf(stderr, "Wait Error.\n\n");
    exit(-1);
      }
      
      // 終了ステータスのチェック
      if(WIFEXITED(status)){
    printf("子プロセス終了");
    code = WEXITSTATUS(status);
    printf("コード : %d\n", code);
      }else{
    printf("wait失敗");
    printf("終了コード : %d\n", status);
      }
      
      i++;
    }    

  }
  printf("親プロセス終了\n");
  return 0;
}

なんかインデントがおかしくなってる
先ほどのHelloと出力するプログラムを10回呼び出すものの例である。