I heard recently that the process model in very early variants of UNIX was quite a bit different to the
fork/exec model used nowadays.
How did it differ from the current state?
If you search for the seminal 1979 paper from Dennis Ritchie, entitled The Evolution of the Unix Time-Sharing System, it covers this (amongst a few other things like incredibly difficult-to-use file-system links, only being able to create directories at boot time, and why the password file has a GECOS field).
First, we'll recap the current model. A process is one type of execution unit within UNIX while a program is a runnable item that lives within a process (when it's running). That distinction is important.
A running process that wants to start a new process will call
fork and this gives you two nearly identical processes running the same program (at the same point), where only one existed before.
At that point, one of them (usually the child) may choose to
exec a new program to perform some other work - this
exec basically replaces the program in the current process with a whole new program.
Should the original process wish to wait until the child exits, it can call
wait to do so.
The old model was a little similar but it only ever had a limited number of processes, one for each of the terminals hooked up to the machine. These processes were created at boot time and there was therefore no
fork. A shell ran in each of these processes, interacting with the user on the given terminal.
When the user specified a program to run, the shell would:
Create a link to the file in the current directory (this has to do with the "incredibly difficult-to-use file-system links" mentioned earlier).
Open the file.
Remove the link.
Copy a small bootstrap program to the top of memory and jump to it.
This bootstrap program would read in the already-open file over the current shell code, then jump to the first location of the command (exec).
After the command had done its work, it called
exit. But this isn't the
exit we know and love nowadays. What this
exit did was simply to reload the shell program into the process in much the same way as the shell had loaded the program in the first place.
At that point, you would be back in the shell, ready to type in another command. As you may imagine, this had no support for pipeslines/filters but, interestingly, had I/O redirection from a very early stage - all the shell had to do was connect the standard handles to specific files rather than the terminal device.