The abstraction provided by the OS of a running program is something we will call a process.
- The process is the OS abstraction for execution
- A process is sometimes called a job or a task or a sequential process
- A sequential process is a program in execution
- A process contains all state for a program in execution
- A process is named using its process ID (PID)
A process has an execution state that indicates what it is currently doing:
- Running: Executing instructions on the CPU
- Ready: Waiting to be assigned to the CPU
- Waiting: Waiting for an event, e.g., I/O completion
The OS data structure representing each process is called the Process Control Block (PCB)
In this interlude, we discuss process creation in UNIX systems. UNIX presents one of the most intriguing ways to create a new process with a pair of system calls: fork() and exec(). A third routine, wait(), can be used by a process wishing to wait for a process it has created to complete.
The fork() system call is used to create a new process.
The process calls the fork() system call, which the OS provides as a way to create a new process. The odd part: the process that is created is an (almost) exact copy of the calling process. That means that to the OS, it now looks like there are two copies of the program p1 running, and both are about to return from the fork() system call.
The newly-created process (called the child, in contrast to the creating parent) doesn’t start running at main(), like you might expect (note, the “hello, world” message only got printed out once); rather, it just comes into life as if it had called fork() itself.
A final and important piece of the process creation API is the exec()
system call. This system call is useful when you want to run a program that is different from the calling program.
The shell is just a user program. It shows you a prompt and then waits for you to type something into it. You then type a command (i.e., the name of an executable program, plus any arguments) into it; in most cases, the shell then figures out where in the file system the executable resides, calls fork() to create a new child process to run the command, calls some variant of exec() to run the command, and then waits for the command to complete by calling wait(). When the child completes, the shell returns from wait() and prints out a prompt again, ready for your next command.