Using the exec() Function in Unix-like Environments

The fork() function creates a new process. Using fork() is simple. Before we call the fork() function, one process, which we term the parent, exists. After we call the fork() function, there are two processes, the parent process and the child process. What can sometimes be confusing is that both processes continue to execute the same program!

However, we can distinguish between the two processes using a decision structure and the return value from the fork() function. The fork() function returns a negative value if it failed. In the child process, the fork() function returns 0. In the parent process, the fork() function returns the PID, which is always a positive integer.

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

int main(void){

    //pid_t is basically an int
    pid_t returnValue;

    if((returnValue = fork()) < 0){
        //error occured
        return -1;
    } 

    if(returnValue  == 0){
        printf("In the new process.\n");
        printf("process ID is %d\n", getpid());
    } 

    if(returnValue > 0){
        printf("In the parent process.\n");
        printf("process ID is %d\n", getpid());
    }

    return 0;

}

Note that PID values are unique; there cannot be two running processes with the same PID.

The exec() function starts a new program that retains the PID of the current process.

While exec() is referred to as being a single function, it is actually a family of functions with slightly different parameter lists and uses.

The simplest function in the exec() family to explain is execve(), since it is the underlying system call. The other functions are in fact wrapper functions.

The execve() function takes three arguments. The first argument is the path argument, this is the path to the executable file we want to execute. The second argument is argv, which is a full list of the arguments we wish to pass the executable file we specified in the first argument. Note that the second argument is an array of pointers, the first element of which should be the path of the program being executed.

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


int main(void){
    
    char *args[] = {"/bin/ls", NULL};

    execve("/bin/ls", args, NULL);


    return 0;

}

Like fork(), the exec() function is declared in the <unistd.h> header.

If exec() succeeds, it does not return to the caller, because exec() completely replaces the caller with the new program.

If exec() fails, it returns -1.

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

int main(void){

    
    char *args[] = {"/bin/ps", NULL};
    
    //check if exec works
    if(execve("/bin/ps", args, NULL) == -1){
        printf("Error with execve");
        return -1;
    }    


    return 0;

}

Remember, a call to exec() should not return, unless there was a problem. Commonly, if there is an error either the program requested doesn’t exist, or it exists but isn’t executable.

In our next program, we will create a child process. In both the parent and  child processes we will run execve() after knowingly passing it bad arguments. We will then examine the error numbers produced using the errno value.

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

int main(void){

    char *args1[] = {"/bin/doesntexist", NULL};
    char *args2[] = {"/home", NULL};

    pid_t forkRVal;
    
    if((forkRVal=fork())<0){
        printf("problem forking");
    }        

    //in child process
    if(forkRVal==0){
        int execReturnVal;
        execReturnVal = execve("/bin/doesntexist", args1, NULL);    
        //execution will only reach this code
        //in the event of failure of execve()
        if(execReturnVal==-1){
            printf("In child process.\t");
            printf("Error no %d\n", errno);
        }
    }

    //in parent process
    if(forkRVal>0){
        int execReturnVal;
        execReturnVal = execve("/home", args2, NULL);
        //check if execve() failed    
        if(execReturnVal==-1){
            printf("In parent process.\t");
            printf("Error no %d\n", errno);
        }
    }

    return 0;

}

The execl() function is a wrapper function for execve(). The execl() function takes as its first argument the pathname of the file to execute. Following this first argument is a variable number of arguments to pass to the program being called. By convention, the second argument should be the name of the program. The list of arguments must be terminated by a NULL value so that execl() can tell where the argument list ends. The program called by execl() inherits the environment specified in the current program’s ENVIRON variable.

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


int main(void){

    int retValue;

    retValue = execl("/bin/date", "date", NULL);
    
    if(retValue==-1){
        printf("Error no %d", errno);
    }

    return 0;

}

If you get the chance, please take a look at my author page on Amazon.com: http://www.amazon.com/Al-Jensen/e/B008MN382O/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s