fork() and exec()
versus Windows' CreateProcess()
while (1) {
write (1, "$ ", 2); // 1 = STDOUT_FILENO
readcommand (0, command, args); // parse user input, 0 = STDIN_FILENO
if ((pid = fork ()) == 0) { // child?
exec (command, args, 0);
} else if (pid > 0) { // parent?
wait (0); // wait for child to terminate
} else {
perror ("Failed to fork\n");
}
}
$ ls
$ ls > tmp1
just before exec insert:
close(1);
creat("tmp1", 0666); // fd will be 1
The kernel always uses the first free file descriptor, 1 in this case.
Could use dup2() to clone a file descriptor to a new number.
The split of process creation into fork and exec turns out to have been an inspired choice, though that might not have been clear at the time; see today's assigned paper.
$ sh < script > tmp1
If for example the file script contains
echo one
echo two
FD inheritance makes this work well.
$ ls f1 f2 nonexistant-f3 > tmp1 2>&1
after creat, insert:
close(2);
creat("tmp1", 0666); // fd will be 2
why is this bad? illustrate what's going on with file descriptors. better:
close(2); dup(1); // fd will be 2(Read Section 3 of "Advanced Programming in the UNIX environment" by W. R. Stevens, esp. Section 3.10) or in bourne shell syntax,
$ ls f1 f2 nonexistant-f3 > tmp1 2>&1
Read Chapter 3 of Advanced Programming in the UNIX Environment by
W. Richard Stevens for a detailed understanding of how file descriptors
are implemented. In particular, read Section 3.10 to understand how
file sharing works.
$ sort < file.txt > tmp1 $ uniq tmp1 > tmp2 $ wc tmp2 $ rm tmp1 tmp2can be more concisely done as:
$ sort < file.txt | uniq | wc
Draw a figure with boxes as programs, and each box containing two ports (STDIN/0 and STDOUT/1).
Advantages of second option: no temporary space, no extra reads/writes to the disk, scheduling visibility to OS
int fdarray[2];
char buf[512];
int n;
pipe(fdarray);
write(fdarray[1], "hello", 5);
n = read(fdarray[0], buf, sizeof(buf));
// buf[] now contains 'h', 'e', 'l', 'l', 'o'
fork(), so this also works:
int fdarray[2];
char buf[512];
int n, pid;
pipe(fdarray);
pid = fork();
if(pid > 0){
write(fdarray[1], "hello", 5);
} else {
n = read(fdarray[0], buf, sizeof(buf));
}
fork() we already have,
to set up a pipe:
int fdarray[2];
if (pipe(fdarray) < 0) panic ("error");
if ((pid = fork ()) == 0) { child (left end of pipe)
close (1);
tmp = dup (fdarray[1]); // fdarray[1] is the write end, tmp will be 1
close (fdarray[0]); // close read end
close (fdarray[1]); // close fdarray[1]
exec (command1, args1, 0);
} else if (pid > 0) { // parent (right end of pipe)
close (0);
tmp = dup (fdarray[0]); // fdarray[0] is the read end, tmp will be 0
close (fdarray[0]);
close (fdarray[1]); // close write end
exec (command2, args2, 0);
} else {
printf ("Unable to fork\n");
}