I'll add some more now that I started:
To just support 0,1,2 you only need pipes really. But since read()/write() in Unix are generic calls to any type of filedescriptors, and pipes are just one example of those, what you often get as 0,1,2 aren't really pipes, but descriptors to tty-devices. They look like pipes for which the other end is handled directly by kernel, but respond to some things you can't do for a pipe... typically you can do ioctl()s on them to tweak the settings of the tty device, which is a virtual device implemented by a tty-driver in kernel. Or you might want to ask the tty-driver how wide is the screen we're printing to so you can do correctly wrap stuff.... and such.
Anyway, a shell that starts a process will basicly do something like this:
Code: Select all
int execute_program(char * name) {
int process = fork();
if(process) {
// in reality we'd handle the possibility of suspends but..
// process is now the PID of the process, and wait() returns
// the return value once that process exists, resuming our shell
// until then it just blocks the shell
return wait(process);
} else {
// the new process from fork() gets return value 0
// first we close all files we don't want to leave open:
int i;
for(i = 3; i < MAXFD, i++) { close(i); }
// we replace the current program image by calling exec()
exec(name);
// that never returns, since it'll no longer be this program at all
}
}
Now, filedescriptors (well most) will get saved by exec(), so 0,1,2 will still
point to the pipes/TTY device that they pointed to in the shell, since we skipped those. We could have done other stuff though, and done more work before we wait():
Code: Select all
int execute_program(char * name, int in, int out, int err) {
int process = fork();
if(process) {
return process;
} else {
int i;
// replace 0,1,2 with "in" "out" and "err"
dup2(in,0); // look in your favourite unix manual for dup2()
dup2(out,1);
dup2(err,2);
// then close the rest and proceed
for(i = 3; i < MAXFD, i++) { close(i); }
exec(name);
}
}
So that way we can then handle things like:
Code: Select all
int runpipe(char * command1, char *command2) {
int fds[2];
pipe(&fds); // likewise, check pipe in UNIX manuals
int p1 = execute_program(command1, 0, fds[1], 2);
int p2 = execute_program(command2, fds[0], 1, 2);
// close the pipe in shell, so we don't keep command2 alive
// when command1 exits
close(fd[0]);
close(fd[1]);
int ret1 = wait(p1);
int ret2 = wait(p2);
return ret1 || ret2;
}
Now everything command1 writes into it's standard out, will go to the the standard input of command2. Other than that it's normal. Both processes will run at the same time. If the pipe's internal buffer gets full, then command1 will block writing until command2 reads it, and if it gets empty, then command2 will block reading until command1 writes some more.
When command1 terminates, it's descriptors will get closed, command2 will get end-of-file and hopefully return as well. If command2 terminates before command1 does, then command1 will get a "broken pipe" signal (or if ignores those EPIPE on write) and hopefully terminate as well.
It's not important in which order one wait()s processes, since if they end before we wait, then OS keeps the return value (and the process as a zombie) until we free it with wait().
If we wanted to redirect with files, we could either start a process which knows how to read/write files, or we could just let the shell play the other party of the pipe reading/writing to files, or we could even open a file and pass that to the new process. It'll still can be read/written so the process doesn't have to care if it doesn't need to anything special with it's terminal.
Just like that, you could even have the pipes be sockets, at which point they could be the telnet connection of some remote user, though normally those are wrapped into a "pseudo tty" to allow full terminal functionality. Shell doesn't need to care though, since it either passes it's own 0,1,2 onwards or creates more redirections..
That's about it when it comes to Unix tty handling and standard inputs and outputs and such.