GNU/Linux >> Linux の 問題 >  >> Linux

システムコール fork() と execv 関数

fork と execv がどのように連携するかを理解する必要があります。

  • fork() は現在のプロセスの複製を作成し、子に 0 を返し、親に childpid を返します
  • fork() は失敗する可能性があり、失敗すると -1 を返します。それを確認してください
  • execv() は複製された親プロセスを新しいプロセスに置き換えます
  • 典型的な fork/exec ペアリングは、子プロセスを新しいプロセスに置き換えます
  • 多くの場合、複数の子を fork し、それらを同時に実行したい
  • しかし、あなたはそれらを連続して実行するように要求しました。つまり、次々と実行されます
  • したがって、2 つ目の作業を開始する前に、最初の作業が完了するまで待つ必要があります
  • したがって、wait() の変形を使用する必要があります。以下の例では、waitpid() を使用して特定の子を待機します

終了 (execv が失敗した場合) には stdlib が必要で、理由を表示するには errno が必要です。

//I'm trying to run two executables consecutively using this c code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

お子様が終了した理由 (コア ダンプ、シグナル、通常の終了) を調べたい場合があるため、この機能を追加しました。

#include <sys/types.h>
#include <sys/wait.h>

//WIFEXITED(status) returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().
//WEXITSTATUS(status) returns the exit status of the child.  This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main().  This macro should only be employed if WIFEXITED returned true.
//WIFSIGNALED(status) returns true if the child process was terminated by a signal.
//WTERMSIG(status) returns the number of the signal that caused the child process to terminate.  This macro should only be employed if WIFSIGNALED returned true.
//WCOREDUMP(status) returns true if the child produced a core dump.  This macro should only be employed if WIFSIGNALED returned true.  This macro is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS).  Only use this enclosed in #ifdef WCOREDUMP ... #endif.
//WIFSTOPPED(status) returns true if the child process was stopped by delivery of a signal; this is only possible if the call was done using WUNTRACED or when the child is being traced (see ptrace(2)).
//WSTOPSIG(status) returns the number of the signal which caused the child to stop.  This macro should only be employed if WIFSTOPPED returned true.
//WIFCONTINUED(status) (since Linux 2.6.10) returns true if the child process was resumed by delivery of SIGCONT.
int
exitreason(pid_t cid, int status)
{
    if( WIFEXITED(status) )
    {
        printf("child %d terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().\n",cid);
        if( WEXITSTATUS(status) )
        {
        printf("child %d exit status %d.  This consists of the least significant 8 bits of the status argument that the child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main().\n",cid,WEXITSTATUS(status));
        }
    }
    if( WIFSIGNALED(status) )
    {
        printf("child %d process was terminated by a signal.\n",cid);
        if( WTERMSIG(status) )
        {
        printf("child %d signal %d that caused the child process to terminate.\n",cid,WTERMSIG(status));
        }
        if( WCOREDUMP(status) )
        {
        printf("child %d produced a core dump.  WCOREDUMP() is not specified in POSIX.1-2001 and is not available on some UNIX implementations (e.g., AIX, SunOS).  Only use this enclosed in #ifdef WCOREDUMP ... #endif.\n",cid);
        }
    }
    if( WIFSTOPPED(status) )
    {
        printf("child %d process was stopped by delivery of a signal; this is only possible if the call was done using WUNTRACED or when the child is being traced (see ptrace(2)).\n",cid);
        if( WSTOPSIG(status) )
        {
        printf("child %d number of the signal which caused the child to stop.\n",cid);
        }
    }
    if( WIFCONTINUED(status) )
    {
        printf("child %d process was resumed by delivery of SIGCONT.\n");
    }
}

そして、コードのどのセクションが親によって処理され、どのセクションが子によって処理されるかを説明するコメントで注釈が付けられたプログラムです。

int main (int argc, char *argv[])
{
    char proc1[] = "/bin/echo"; //"./prcs1";
    char proc2[] = "/bin/echo"; //"./prcs2";
    pid_t cid1, cid2, cidX;
    int status=0;
    int waitoptions = 0;
    //WNOHANG    return immediately if no child has exited.
    //WUNTRACED  also return if a child has stopped (but not traced via ptrace(2)).  Status for traced children which have stopped is provided even if this option is not specified.
    //WCONTINUED also return if a stopped child has been resumed by delivery of SIGCONT.
    int res;

    if( (cid1 = fork()) == 0 ) //child1
    {
        printf("in child1\n");
        if( (res = execv(proc1, &argv[1])) < 0 ) // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
        {
        printf("error: child1: %d exec failed %d\n", cid1, errno);
        printf("error: cannot execv %s\n",proc1);
        exit(91); //must exit child
        }
    }
    else if( cid1 > 0 ) //cid>0, parent, waitfor child
    {
        cidX = waitpid(cid1, &status, waitoptions);
        printf("child1: %d res %d\n", cid1, res);
        exitreason(cid1, status);
    }
    else //cid1 < 0, error
    {
        printf("error: child1 fork failed\n");
    }

    if( (cid2 = fork()) == 0 ) //child2
    {
        printf("in child2\n");
        if( (res = execv(proc2, &argv[1])) < 0 ) // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
        {
        printf("error: child2: %d exec failed %d\n", cid2, errno);
        printf("error: cannot execv %s\n",proc2);
        exit(92); //must exit child
        }
    }
    else if( cid2 > 0 ) //cid>0, parent, waitfor child
    {
        cidX = waitpid(cid2, &status, waitoptions);
        printf("child2: %d res %d\n", cid2, res);
        exitreason(cid2, status);
    }
    else //cid2 < 0, error
    {
        printf("error: child2 fork failed\n");
    }
}

いくつかの問題があります。まず、2 つのプログラムのみを実行する場合は、fork() を呼び出すだけです。 一度。次に、親プロセスで 1 つのプログラムを実行し、子プロセスで 1 つのプログラムを実行します。第二に、あなたは argv を構築しています execv に渡される配列 間違って。最初のエントリは実行可能ファイル名にする必要があります。次のようにします:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
    pid_t i = fork();
    if (i == 0)
    {
        execv("./prcs1", (char *[]){ "./prcs1", argv[1], NULL });
        _exit(1);
    }
    else if (i > 0)
    {
        execv("./prcs2", (char *[]){ "./prcs2", argv[0], NULL });
        _exit(2);
    }
    else
    {
        perror("fork failed");
        _exit(3);
    }
}

この例ではエラー チェックを行わないことに注意してください。


fork() についてはあまり読んでいないと思います。

fork() を呼び出したとき 、フォークから同じコードを実行する子プロセスを作成します。

fork() 3 種類の値を返します

  • エラーを示すネガティブ
  • 親プロセスにいることを示し、値は子プロセス ID を示します
  • あなたが子プロセスにいることを示すゼロ

コードは次のようになります。

#include <stdio.h>
#include <unistd.h>

int main (int argc, char *argv[])
{

    int ret = fork();
    if(ret==0)
    {
       //child process
       execv("./prcs1", &argv[1]); // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
       printf("EXECV Failed from child\n");
    }
    else if(ret>0)
    {
       //parent process
       execv("./prcs2", argv);
       printf("EXECV Failed from parent\n");
    }
    else
    {
       //you will come here only if fork() fails.
       printf("forkFailed\n");
    }
    return 0;
}

Linux
  1. システムとサーバーのステータス

  2. スーパーブロック、iノード、Dentry、およびファイル?

  3. Bash で関数を作成して呼び出す方法

  1. Linux プロセス – プロセス ID、fork、execv、wait、waitpid C 関数

  2. C++ で C 関数を呼び出す方法、C で C++ 関数を呼び出す方法 (C と C++ の混合)

  3. EINTR をチェックして関数呼び出しを繰り返すのはいつですか?

  1. execv() と fork() の時間の無駄

  2. system() 関数の文字列の長さ制限

  3. Linux でのライブラリ呼び出しとシステム呼び出しの違いは何ですか?