OpenVMS
HP C ランタイム・ライブラリ・
リファレンス・マニュアル (上巻)


前へ 次へ 目次 索引


5.2.1 exec の処理

exec関数では, LIB$SPAWN ルーチンを使用してサブプロセスを生成し,サブプロセス内で子イメージを起動します。この子プロセスは,定義されているすべての論理名やコマンド・ライン・インタプリタ・シンボルなど,親の環境を継承します。

デフォルトでは,子プロセスは親プロセスのデフォルト (作業) ディレクトリも継承します。ただし, decc$set_child_default_dir関数を使用して,子プロセスの実行開始時のデフォルト・ディレクトリを設定できます。 decc$set_child_default_dir関数の詳細については,『HP C ランタイム・ライブラリ・リファレンス・マニュアル (下巻)』の「リファレンス・セクション」を参照してください。

exec関数は論理名 VAXC$EXECMBX を使用して,親と子の間の通信を行います。この論理名は親イメージのコンテキストの内部に存在しなければなりません。

exec関数は次の情報を子に渡します。

すべての情報が子に転送されると, execの処理は終了します。親プロセス内の制御は, vforkによって保存されたアドレスに返され,子のプロセス ID は親に返されます。

シグナル動作および SIGCHLD シグナルの詳細については, 第 4.2.4 項 を参照してください。

5.2.2 exec のエラー条件

LIB$SPAWN がサブプロセスを生成できない場合は, exec関数は異常終了します。エラーの原因となる可能性のある条件としては,サブプロセス・クォータの超過や,親と子の間でコンテキスト・メールボックスによる通信が不能であることの検出などがあります。一部のクォータは,超過しても LIB$SPAWN がエラーになることはありませんが, LIB$SPAWN が待ち状態になり,その結果,親プロセスがハングする可能性があります。このようなクォータの例としては,オープン・ファイル・リミット・クォータがあります。

オープン・ファイル・リミット・クォータは少なくとも 20 ファイルに設定する必要があり,平均値はプログラムで同時に実行するプロセスの数の 3 倍です。一度に複数のオープン・パイプを使用する場合や,一度に複数のファイルに対して I/O を実行する場合は,このクォータをさらに大きくする必要があります。このクォータを増大する必要があるかどうかについては,システム管理者にお問い合わせください。

exec関数が異常終了した場合, - 1 という値が返されます。このような障害が発生した後,親は exit関数または _exit関数を呼び出すことが期待されます。どちらの関数も親の vfork呼び出しに戻り,この関数呼び出しは子のプロセス ID を返します。この場合, exec関数から返される子プロセス ID は 0 より小さくなります。 exit関数の詳細については,『HP C ランタイム・ライブラリ・リファレンス・マニュアル (下巻)』「リファレンス・セクション」を参照してください。

5.3 プロセスの同期化

親プロセスが終了すると,子プロセスも終了します。したがって,親プロセスは終了する前に,子プロセスの状態を確認する必要があります。この処理は, HP C RTL 関数 waitを使用して行います。

5.4 プロセス間通信

親プロセスと子プロセスが通信するチャネルはパイプと呼ばれます。パイプを作成するには, pipe関数を使用します。

5.5 プログラムの例

例 5-1 は,子プロセスでイメージを実行するための基本手順を示しています。 例 5-1 の子プロセスはメッセージを 10 回プリントします。

例 5-1 子プロセスの生成

/*      chap_5_exec_image.c     */ 
 
/* This example creates the child process.  The only    */ 
/* functionality given to the child is the ability to   */ 
/* print a message 10 times.                            */ 
 
#include <climsgdef.h>  /* CLI status values  */ 
#include <stdio.h> 
#include <perror.h> 
#include <processes.h> 
#include <stdlib.h> 
 
static const char *child_name = "chap_5_exec_image_child.exe" ; 
 
main() 
{ 
   int status, 
       cstatus; 
 
   /* NOTE:                                            */ 
   /*    Any local automatic variables, even those     */ 
   /*    having the volatile attribute, may have       */ 
   /*    indeterminant values if they are modified     */ 
   /*    between the vfork() call and the matching     */ 
   /*    exec() call.                                  */ 
 
(1)   if ((status = vfork()) != 0) {   
       /* This is either an error or                   */ 
       /* the "second" vfork return, taking us "back"  */ 
       /* to parent mode.                              */ 
(3)       if (status < 0)   
            printf("Parent - Child process failed\n"); 
       else { 
            printf("Parent - Waiting for Child\n"); 
(4)            if ((status = wait(&cstatus)) == -1)  
               perror("Parent - Wait failed"); 
(5)            else if (cstatus == CLI$_IMAGEFNF)    
               printf("Parent - Child does not exist\n"); 
            else 
               printf("Parent - Child final status: %d\n", cstatus); 
        } 
    } 
(2)    else {  /* The FIRST Vfork return is zero, do the exec */ 
           printf("Parent - Starting Child\n"); 
           if ((status = execl(child_name, 0)) == -1) { 
               perror("Parent - Execl failed"); 
               exit(EXIT_FAILURE); 
         } 
    } 
} 
 
---------------------------------------------------------- 


/*      CHAP_5_EXEC_IMAGE_CHILD.C                    */ 
 
/* This is the child program that writes a message   */ 
/* through the parent to "stdout"                    */ 
 
#include <stdio.h> 
 
main() 
{ 
    int i; 
 
    for (i = 0; i < 10; i++) 
        printf("Child - executing\n"); 
    return (255) ;    /* Set an unusual success stat */ 
} 

例 5-1 の補足説明:

  1. vfork関数は, exec呼び出しの戻りアドレスを設定するために呼び出されます。
    vfork関数は通常, if文の式で使用されます。この構造では,1 つの戻り値が 0 で,もう 1 つが 0 以外であるため, vforkの 2 つの戻りアドレスを利用できます。

  2. vforkを最初に呼び出すと,戻り値 0 が返され,親は vfork呼び出しに関連付けられた else句を実行し,その結果, execlが呼び出されます。

  3. exec関数が異常終了すると,負の子プロセス ID が返されます。これらの条件に関して戻り値が確認されます。

  4. wait関数は,親プロセスと子プロセスの同期をとるために使用されます。

  5. exec関数は,子プロセスで起動されるイメージが存在しない場合でも,この時点まで正常終了を示す可能性があるため,親が子プロセスの戻り状態を調べて,定義済み状態 CLI$_IMAGEFNF (ファイルが見つからない) が返されていないかどうか確認します。

例 5-2 では,親は子プロセスに引数を渡します。

例 5-2 子プロセスへの引数の引き渡し

/*       CHAP_5_CHILDARG.C                                           */ 
 
/* In this example, the arguments are placed in an array, gargv,     */ 
/* but they can be passed to the child explicitly as a zero-         */ 
/* terminated series of character strings. The child program in this */ 
/* example writes the arguments that have been passed it to stdout.  */ 
 
#include <climsgdef.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <perror.h> 
#include <processes.h> 
 
const char *child_name = "chap_5_childarg_child.exe" ; 
 
main() 
{ 
    int status, 
        cstatus; 
    char *gargv[] = 
    {"Child", "ARGC1", "ARGC2", "Parent", 0}; 
 
    if ((status = vfork()) != 0) { 
        if (status < -1) 
            printf("Parent - Child process failed\n"); 
        else { 
            printf("Parent - waiting for Child\n"); 
            if ((status = wait(&cstatus)) == -1) 
                perror("Parent - Wait failed"); 
            else if (cstatus == CLI$_IMAGEFNF) 
                printf("Parent - Child does not exist\n"); 
            else 
                printf("Parent - Child final status: %x\n", 
                       cstatus); 
        } 
    } 
    else { 
        printf("Parent - Starting Child\n"); 
        if ((status = execv(child_name, gargv)) == -1) { 
            perror("Parent - Exec failed"); 
            exit(EXIT_FAILURE); 
        } 
    } 
} 
 
-------------------------------------------------------- 
/*       CHAP_5_CHILDARG_CHILD.C                      */ 
 
/* This is a child program that echos its arguments   */ 
 
#include <stdio.h> 
 
main(argc, argv) 
    int argc; 
    char *argv[]; 
{ 
    int i; 
 
    printf("Program name: %s\n", argv[0]); 
 
    for (i = 1; i < argc; i++) 
        printf("Argument %d: %s\n", i, argv[i]); 
    return(255) ; 
} 

例 5-3 は, wait関数を使用して,同時に実行される複数の子の最終状態を確認する方法を示しています。

例 5-3 子プロセスの状態の確認

/*        CHAP_5_CHECK_STAT.C                                   */ 
 
/* In this example 5 child processes are started.  The wait()   */ 
/* function is placed in a separate for loop so that it is      */ 
/* called once for each child. If wait() were called within     */ 
/* the first for loop, the parent would wait for one child to   */ 
/* terminate before executing the next child. If there were     */ 
/* only one wait request, any child still running when the      */ 
/* parent exits would terminate prematurely.                    */ 
 
#include <climsgdef.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <perror.h> 
#include <processes.h> 
 
const char *child_name = "chap_5_check_stat_child.exe" ; 
 
main() 
{ 
    int status, 
        cstatus, 
        i; 
 
    for (i = 0; i < 5; i++) { 
        if ((status = vfork()) == 0) { 
            printf("Parent - Starting Child %d\n", i); 
            if ((status = execl(child_name, 0)) == -1) { 
                perror("Parent - Exec failed"); 
                exit(EXIT_FAILURE); 
            } 
        } 
        else if (status < -1) 
            printf("Parent - Child process failed\n"); 
    } 
 
    printf("Parent - Waiting for children\n"); 
 
    for (i = 0; i < 5; i++) { 
        if ((status = wait(&cstatus)) == -1) 
            perror("Parent - Wait failed"); 
        else if (cstatus == CLI$_IMAGEFNF) 
            printf("Parent - Child does not exist\n"); 
        else 
            printf("Parent - Child %X final status: %d\n", 
                   status, cstatus); 
    } 
} 

例 5-4 は, pipe関数と dup2関数を使用して,特定のファイル記述子を通じて親プロセスと子プロセスの間で通信する方法を示しています。 #defineプリプロセッサ・ディレクティブは,プリプロセッサ定数 inpipeoutpipeをファイル記述子 11 と 12 の名前として定義します。

例 5-4 パイプによる通信

/*        CHAP_5_PIPE.C                                         */ 
 
/* In this example, the parent writes a string to the pipe for  */ 
/* the child to read.  The child then writes the string back    */ 
/* to the pipe for the parent to read.  The wait function is    */ 
/* called before the parent reads the string that the child has */ 
/* passed back through the pipe.  Otherwise, the reads and      */ 
/* writes will not be synchronized.                             */ 
 
#include <perror.h> 
#include <climsgdef.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <processes.h> 
#include <unixio.h> 
 
#define inpipe 11 
#define outpipe 12 
 
const char *child_name = "chap_5_pipe_child.exe" ; 
 
main() 
{ 
    int pipes[2]; 
    int mode, 
        status, 
        cstatus, 
        len; 
    char *outbuf, 
        *inbuf; 
 
    if ((outbuf = malloc(512)) == 0) { 
        printf("Parent - Outbuf allocation failed\n"); 
        exit(EXIT_FAILURE); 
    } 
 
    if ((inbuf = malloc(512)) == 0) { 
        printf("Parent - Inbuf allocation failed\n"); 
        exit(EXIT_FAILURE); 
    } 
    if (pipe(pipes) == -1) { 
        printf("Parent - Pipe allocation failed\n"); 
        exit(EXIT_FAILURE); 
    } 
 
    dup2(pipes[0], inpipe); 
    dup2(pipes[1], outpipe); 
    strcpy(outbuf, "This is a test of two-way pipes.\n"); 
 
    status = vfork(); 
 
    switch (status) { 
    case 0: 
        printf("Parent - Starting child\n"); 
        if ((status = execl(child_name, 0)) == -1) { 
            printf("Parent - Exec failed"); 
            exit(EXIT_FAILURE); 
        } 
        break; 
 
    case -1: 
        printf("Parent - Child process failed\n"); 
        break; 
 
    default: 
        printf("Parent - Writing to child\n"); 
  
        if (write(outpipe, outbuf, strlen(outbuf) + 1) == -1) { 
            perror("Parent - Write failed"); 
            exit(EXIT_FAILURE); 
        } 
        else { 
            if ((status = wait(&cstatus)) == -1) 
                perror("Parent - Wait failed"); 
 
            if (cstatus == CLI$_IMAGEFNF) 
                printf("Parent - Child does not exist\n"); 
            else { 
                printf("Parent - Reading from child\n"); 
                if ((len = read(inpipe, inbuf, 512)) <= 0) { 
                    perror("Parent - Read failed"); 
                    exit(EXIT_FAILURE); 
                } 
                else { 
                    printf("Parent: %s\n", inbuf); 
                    printf("Parent - Child final status: %d\n", 
                            cstatus); 
                } 
            } 
        } 
        break; 
    } 
}  
 
------------------------------------------------------------------ 
/*        CHAP_5_PIPE_CHILD.C                                   */ 
 
/* This is a child program which reads from a pipe and writes   */ 
/* the received message back to its parent.                     */ 
 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
 
#define inpipe 11 
#define outpipe 12 
 
main() 
{ 
    char *buffer; 
    int len; 
 
    if ((buffer = malloc(512)) == 0) { 
        perror("Child - Buffer allocation failed\n"); 
        exit(EXIT_FAILURE); 
    } 
 
    printf("Child - Reading from parent\n"); 
    if ((len = read(inpipe, buffer, 512)) <= 0) { 
        perror("Child - Read failed"); 
        exit(EXIT_FAILURE); 
    } 
    else { 
        printf("Child: %s\n", buffer); 
        printf("Child - Writing to parent\n"); 
        if (write(outpipe, buffer, strlen(buffer) + 1) == -1) { 
            perror("Child - Write failed"); 
            exit(EXIT_FAILURE); 
        } 
    } 
    exit(EXIT_SUCCESS); 
} 


前へ 次へ 目次 索引