Today's Outline
- processes
- threads
- linux implementation
Running Programs
- what does the execve system call (or other
calls in the exec family) do?
- stops executing the current program,
- reads an executable file into memory,
- starts executing the new program
- when we want to avoid step (1), we call fork first
- this means two programs are running "at the same time"
Program Execution: Processes
- both program executions could be executing the same code,
but doing different things (otherwise we would have the creation
of an infinite number of processes, which is likely to get your
sysadmin unhappy at you)
- or the two program executions could be executing different code
- either way, each program needs its own state:
- private read/write memory
- address space
- open files
- program counter, stack pointer, and other registers
- pointers to shared resources
- such a program execution, with its state, is called a process
Program Execution: Threads
- when a process is created, lots of state must be copied or created
- some program executions can:
- share code
- share most of the state
- in such cases, it is most efficient to create a thread,
perhaps using pthread_create:
- threads within a process share global variables, including
address space and all pointers
- threads within a process do not share local (stack) variables,
including program counter, stack pointer, and other registers
Running Programs in Parallel
- a single CPU can only execute one instruction at a time
- therefore, a single CPU cannot execute multiple processes
in parallel
- a single CPU can, however:
- interrupt the currently running process
- save its state
- start executing another (suspended) process
- if this is done quickly enough, it gives the illusion
that multiple processes are executing at once
- this illusion is also a good way for people to understand
what is happening, so we treat it as a reality
Running Programs: OS view
- the state of a process must be captured in a data structure, the
process descriptor (a task_struct in Linux)
- part of the job of the operating system is to manage process
descriptors for all the processes
- on a system with a single CPU, when the OS code is
executing, no other process can be executing at the same time
- nonetheless, the process that was executing before the kernel
code began execution is the current process
- this is the process (if any) that executed the system call, or that
was interrupted, or had an error
- once the kernel code completes, it must transfer control to some
other process, either the current process, or some other process
Process States
- part of the process descriptor records whether the process is in
a state that is ready to run -- runnable, or TASK_RUNNING
in Linux
- what else can the process be doing?
- blocked waiting for some condition, e.g. for network data to arrive
or for a certain length of time to pass
(TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE in Linux)
- done executing, either permanently
(TASK_ZOMBIE in Linux), or until a signal is received
(TASK_STOPPED in Linux)
Process Termination
- calling exit
- receiving a signal -- e.g. segmentation fault, bus error, floating
point error, user interrupt (Control-C), some I/O errors (if turned on),
kernel resource exhaustion, etc.
- return all resources: close open files, deallocate memory
and page tables, unclaim all shared resources
- notify parent (creater) process
- remove process descriptor, free process identifier
- schedule a new task
Process Hierarchy
- in Unix systems, every process (except init, which is process 1)
has a parent process which created it, and has zero or more
child processes which it created
- a process is not allowed to die until its parent has
had a chance to harvest the return
code (the argument to the exit call)
- such a process that cannot die or be killed (but cannot execute, either)
is known as a zombie
- in non-linux Unix systems there are ways to detach child
processes so the parent doesn't need to call wait