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:
STARTUPINFO si={0};
PROCESS_INFORMATION pi;
CreateProcess(
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. */
execlp("/bin/echo","foo");
} /* 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.