Creating Processes, Threads, etc.

CS 321 Lecture, Dr. Lawlor

Download example programs for windows and UNIX here.

CreateProcess: Sane, yet Annoying

CreateProcess is the Windows way to create processes.  It does the obvious thing--you give it the path to an exe, and it runs it.  CreateProcess hence requires a zillion parameters to override every possible aspect of the new process (e.g., the stack size, the open files, security attributes), most of which you don't care about.

Here's the guts of a typical call to CreateProcess.  Note how many of the parameters are actually useful to us:
0, /* program name */
"process_win.exe a b c", /* command line */
0,0,false,0,0,0, /* optional stuff */
&si,&pi /* startup information, process information */
Note how of all that crap, there's actually only one parameter we care about--the command line.  This seems like a waste.

The madness of UNIX fork()

fork is the UNIX way to create processes.  It's a very zen process creation routine.  It takes no parameters.  Who, then, will the new process be?  It will be you.  A second copy of you, the running program.  Your second ("forked") copy can then change its own stack size, open files, security attributes, and so on--it's always possible to change these things anyway, so no new parameters are needed!

Here's a typical call to fork:
	pid_t p=fork();
In the newly-forked "child" process, p is zero.  In the original or "parent" process, p is the "process ID" of the freshly-cloned child process.  Everything else about the parent and child processes is identical.
#include <stdio.h>
#include <unistd.h> /* for fork */

int main(int argc,char *argv[]) {
printf("Starting main routine\n");
fflush(stdout); /*<- avoid duplicate prints */
pid_t p=fork();
printf("Fork returned %d\n",p);
return 0;
(executable NetRun link)

This prints:
Starting main routine    (from the original)
Fork returned 0 (from the child)
Fork returned 6658 (from the parent)
Here's a more complete version of a fork-and-work example: (executable NetRun link)

Here's how Abbot and Costello would describe fork().  Abbot plays the part of the OS.

    Costello: Hey OS, can you make me a new process?
    Abbot: Sure.  You're a new process.
(Abbot waves his magic wand, and a new clone of Costello is created, standing in front of the original.)
    Clone-Costello (to Abbot): What do you mean?  Where's my new process?
    Abbot (to Clone): You are.
    Clone-Costello (to Abbot):  I'm what?
    Abbot (to Clone): The new process.
    Clone-Costello (to Abbot, looking around):  Where?
    Original-Costello (to Abbot, waving from behind Clone): Thanks!  He looks great!
    Abbot (to Original): You're welcome.

Of course, in reality the clone knows what's happening, at least assuming the programmer's done his job!

Note: "while(1) fork();" is known as a "fork bomb", and creates an exponentially growing number of processes.  NetRun is finally immune to this.  Try it!

The other half of UNIX process setup: exec

So UNIX fork() is a "pure" process creation routine.  It doesn't actually change executable files the way CreateProcess does.  Instead, to switch executable files without creating a new process, you use one of the exec family of functions.  These functions overwrite the current executable with some other executable, and they never return (if they work!).

A typical way fork and exec are used together is like this:
	pid_t p=fork();
if (p==0) { /* I'm the child process, so I exec the "/bin/echo" program. */
} /* else I'm the parent, so I go about my business */
UNIX is quite funny in that process creation and executable switching are totally decoupled--you can just fork off a bunch of copies of your program (e.g., to take advantage of multiple CPUs), or you can exec in a new executable without creating a new process.