foo can
tranform itself into an executing instance of a different
program bar. This is important because, after a
fork, we often would like the child to transform into a
different program.
There are six system calls to make this tranformation of a
process -- all variations on the same basic idea --- and we
refer to them generically as exec. In fact
man -s2 exec gets you the documentation on all
the functions, despite the fact that none of the functions
is actually called "exec". You give
exec a program name, command line arguments
and, optionally, an environement (i.e. the environment
variable name-value pairs) ... essentially everything you'd
need to launch a new program. The differences between the
six execs is in the details of how you want to
pass that info. Section 8.10 explains all six very nicely.
#include <unistd.h> int execl(const char *path, const char *arg0, ... /* const char *argn, (char *)0 */); int execv(const char *path, char *const argv[]); int execle(const char *path, const char *arg0, ... /* const char *argn, (char *)0,char *const envp[]*/); int execve(const char *path, char *const argv[], char *const envp[]); int execlp(const char *file, const char *arg0, ... /* const char *argn, (char *)0 */); int execvp(const char *file, char *const argv[]);Let's look at
execlp first. The first argument
file
is the name of the program that the current process will
transform into. The current environment's PATH
variable will be used to find the program from the name.
The subsequent arguments (and there may be as many as you
like) will become the argv values for the new
program. Note that the first argument, file,
will not be put in argv, so you must echo it to
get the usual argv[0] being the program name. The last
argument must be NULL ... that essentially tells
exec that the argument list is done.
Look at this example, in which a foo
process changes itself
into a bar process by calling exec.
foo.c |
bar.c |
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("I am %i! I am foo! My argv is:", getpid());
int i;
for(i = 0; i < argc; ++i)
printf(" %s",argv[i]);
printf("\n");
execlp("./bar","./bar","fi","fi","fo",NULL);
printf("Die Bart, die!\n");
return 0;
}
|
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("I am %i! I am bar! My argv is:", getpid());
int i;
for(i = 0; i < argc; ++i)
printf(" %s",argv[i]);
printf("\n");
return 0;
}
|
The output of this program is interesting primarily because we see that the process ID does not change. So we don't get a new process, the old process takes on a new role.
/*
plotter: This program plots the function
f(x) = sin(x)*sin(5*x)*10/(1+x)
in the range 0..B, where B is read from stdin. You may enter as
many B values as you like, each pops up a new plot window.
The program spawns a gnuplot process to display the plot.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
/* Loop over each user input B and plot f(x) in the range 0..B. */
double B;
while(scanf("%lf",&B) == 1)
{
/* Write file of plot commands */
FILE *fq = fopen("commands","w");
fprintf(fq,"set samples 500\n");
fprintf(fq,"plot [x=0:%f] sin(x)*sin(5*x)*10/(1+x) smooth csplines\n",B);
fclose(fq);
/* Spawn a gnuplot process to plot data file */
if (fork() == 0)
if (execlp("gnuplot","gnuplot","-persist","commands",NULL) < 0)
{
fprintf(stderr,"Error!\n");
exit(1);
}
}
return 0;
}
sssk below copies stdin to stdout while removing
whitespace. We can provide a new program that does the same
thing, but gets an input file name from the command line, and
produces output in a file of the same name, but
with .mod on the end ... without
changing sssk, or knowing anything about how it
works:
sssk.c |
wrapper.c |
|
/*
This program demonstrates how we can use a program that requires
input from stdin and output from stdout (sssk in this case) with
code to set stdin/stdout appropriately and then execs.
*/
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
/* Check for sufficient agruments! */
if (argc < 2)
{
fprintf(stderr,"usage: wrapper <file>\n");
return 1;
}
/* Set stdin to the file given by argv[1]. */
int fdin;
if ((fdin = open(argv[1],O_RDONLY)) < 0)
{
fprintf(stderr,"wrapper: \"%s\" not found!\n",argv[1]);
return 2;
}
dup2(fdin,0);
/* Set stdout to the file given by argv[1] with .mod at the end. */
char *buff = (char*)malloc(strlen(argv[1]) + 5);
buff[0] = '\0';
strcpy(buff,argv[1]);
strcat(buff,".mod");
int fdout;
if ((fdout = open(buff,O_WRONLY|O_CREAT)) < 0)
{
fprintf(stderr,"wrapper: \"%s\" could not be opened!\n",argv[1]);
return 3;
}
dup2(fdout,1);
/* exec into sssk ... if exec retuns, it failed! */
execlp("./sssk","sssk",NULL);
fprintf(stderr,"wrapper: exec failed!\n");
return 4;
}
|
It's worth noting that you can do this kind of thing really easily with shell scripts.