System call fork() and execv function

61,172

Solution 1

You have a couple of problems. First, if you only want to run two programs, you only need to call fork() once. Then run one program in the parent process and one in the child. Second, you're constructing the argv array to be passed to execv incorrectly. The first entry should be the executable name. Do something like:

#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);
    }
}

Note that this example does no error checking.

Solution 2

You need to understand how fork and execv work together.

  • fork() makes a duplicate of the current process, returning 0 to child, childpid to parent
  • fork() can fail, and returns -1 on failure, check for that
  • execv() replaces the duplicated parent process with a new process
  • typical fork/exec pairing replaces the child process with a new process
  • often you fork more than one child, and want them to run simultaneously,
  • however, you asked for them to run consecutively, that is one after another
  • thus, you need to wait for the first to complete before starting the second
  • thus you need to use some variant of wait(), example below uses waitpid() to wait for specific child

You need stdlib for exit (in case execv fails), and errno, to print the reason,

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

You may want to examine the reason your child exited (core dump, signal, normal exit), thus I have added this function,

#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");
    }
}

And here is your program annotated with comments explaining which sections of code are processed by the parent, and which by the child(ren).

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");
    }
}

Solution 3

You haven't had much reading on fork() I guess.

when you call fork(), it creates a child process which will run the same code from fork.

fork() returns three kind of values

  • negative which shows an error
  • positive which says you are in parent process and value shows childprosess ID
  • zero which says you are in child process.

your code should look like this.

#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;
}

Solution 4

The exec family will only return if the call fails.

Since you do not check the return value of fork you will call execv in parent and child process.

Check the return value: if it is 0 you are in the child process, if it is greater than zero then you are in the parent process. Less than zero means the fork failed.

Share:
61,172
Mike
Author by

Mike

Updated on September 13, 2020

Comments

  • Mike
    Mike over 3 years

    I'm trying to run two executables consecutively using this c code:

    #include <stdio.h>
    #include <unistd.h>
    
    int main (int argc, char *argv[])
    {
        fork();
        execv("./prcs1", &argv[1]); // GIVE ADDRESS OF 2nd element as starting point to skip source.txt
        fork();
        execv("./prcs2", argv);
        printf("EXECV Failed\n");
    }
    

    The program exits after the first execv() call despite the fork, it never gets to the second execv(). I've tried calling wait() after the first fork but I'm not sure that's what it's missing.

    Any ideas why control doesn't return to the parent after the child exits?

  • Markku K.
    Markku K. over 10 years
    Actually, execv() never returns if it succeeds. See linux.die.net/man/3/execv
  • LostBoy
    LostBoy over 10 years
    Well it returns on failure, but you are right ... I'll edit the answer
  • Kerrek SB
    Kerrek SB over 10 years
    And also an entirely unreasonable return 0 :-S
  • Carl Norum
    Carl Norum over 10 years
    Yeah; just trying to shut up the system. I guess if an execv fails it should be there.
  • Kerrek SB
    Kerrek SB over 10 years
    Any way of reaching that point is an error... but to be safe, I'd also add _exit(EXIT_FAILURE) after the child's exec.
  • Carl Norum
    Carl Norum over 10 years
    OK, improved based on comments.
  • Kerrek SB
    Kerrek SB over 10 years
    Not wanting to nitpick, but after a recent series of deadlocks thanks to tcmalloc and pthread_atfork, I would really insist to use _exit in the child. It's just very dangerous to go on after a fork. I suppose if the application is guaranteed to be single-threaded (does your allocator promise that? do you know?), you might be fine, but why take the risk.
  • Kerrek SB
    Kerrek SB over 10 years
    Can you promise that your memory allocator hasn't spun up threads that are holding locks?
  • Akash
    Akash over 6 years
    +1 for - run the same code from fork. This is important as the code doesn't start fresh but whole data and stack are copied while the text remains the same in the Region table with a new copy of Program Counter. I think this is the most relevant answer.
  • Will
    Will over 3 years
    Bullet #3: You could say execv() replaces the child process with a new program