The created child process does not have to run the same program as the parent process does. The exec type system calls allow a process to run any program files, which include a binary executable or a shell script. On this page, we only discuss one such system call: execvp(). The execvp() system call requires two arguments:
Note that this argument must be terminated by a zero.int main(int argc, char **argv)
When execvp() is executed, the program file given by the first argument will be loaded into the caller's address space and over-write the program there. Then, the second argument will be provided to the program and starts the execution. As a result, once the specified program file starts its execution, the original program in the caller's address space is gone and is replaced by the new program.
execvp() returns a negative value if the execution fails (e.g., the request file does not exist).
The following is an example (in file shell.c). Click here to download a copy.
#include <stdio.h> #include <sys/types.h> void parse(char *line, char **argv) { while (*line != '\0') { /* if not the end of line ....... */ while (*line == ' ' || *line == '\t' || *line == '\n') *line++ = '\0'; /* replace white spaces with 0 */ *argv++ = line; /* save the argument position */ while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n') line++; /* skip the argument until ... */ } *argv = '\0'; /* mark the end of argument list */ } void execute(char **argv) { pid_t pid; int status; if ((pid = fork()) < 0) { /* fork a child process */ printf("*** ERROR: forking child process failed\n"); exit(1); } else if (pid == 0) { /* for the child process: */ if (execvp(*argv, argv) < 0) { /* execute the command */ printf("*** ERROR: exec failed\n"); exit(1); } } else { /* for the parent: */ while (wait(&status) != pid) /* wait for completion */ ; } } void main(void) { char line[1024]; /* the input line */ char *argv[64]; /* the command line argument */ while (1) { /* repeat until done .... */ printf("Shell -> "); /* display a prompt */ gets(line); /* read in the command line */ printf("\n"); parse(line, argv); /* parse the line */ if (strcmp(argv[0], "exit") == 0) /* is it an "exit"? */ exit(0); /* exit if it is */ execute(argv); /* otherwise, execute the command */ } }
Function parse() takes an input line and returns a zero-terminated array of char pointers, each of which points to a zero-terminated character string. This function loops until a binary zero is found, which means the end of the input line line is reached. If the current character of line is not a binary zero, parse() skips all white spaces and replaces them with binary zeros so that a string is effectively terminated. Once parse() finds a non-white space, the address of that location is saved to the current position of argv and the index is advanced. Then, parse() skips all non-whtitespace characters. This process repeats until the end of string line is reached and at that moment argv is terminated with a zero.
For example, if the input line is a string as follows:
Function parse() will return array argv[] with the following content:"cp abc.CC xyz.TT"
Function execute() takes array argv[], treats it as a command line arguments with the program name in argv[0], forks a child process, and executes the indicated program in that child process. While the child process is executing the command, the parent executes a wait(), waiting for the completion of the child. In this special case, the parent knows the child's process ID and therefore is able to wait a specific child to complete.
The main program is very simple. It prints out a command prompt, reads in a line, parses it using function parse(), and determines if the name is "exit". If it is "exit", use exit() to terminate the execution of this program; otherwise, the main uses execute() to execute the command.