Tanenbaum's chapter title is ``Processes and Threads''. I prefer to add the word management. The subject matter is processes, threads, scheduling, interrupt handling, and IPC (InterProcess Communication--and Coordination).
Definition: A process is a program in execution.
Even though in actuality there are many processes running at once, the OS gives each process the illusion that it is running alone.
Think of the individual modules that are input to the linker. Each numbers its addresses from zero; the linker eventually translates these relative addresses into absolute addresses. That is the linker provides to the assembler a virtual memory in which addresses start at zero.
Virtual time and virtual memory are examples of abstractions
provided by the operating system to the user processes so that the
latter ``sees'' a more pleasant virtual machine than actually exists.
From the users or external viewpoint there are several mechanisms
for creating a process.
But looked at internally, from the system's viewpoint, the second
method dominates. Indeed in unix only one process is created at
system initialization (the process is called init); all the
others are children of this first process.
Why have init? That is why not have all processes created via
method 2? Again from the outside there appear to be several termination
mechanism.
And again, internally the situation is simpler. In Unix
terminology, there are two system calls kill and
exit that are used. Kill (poorly named in my view) sends a
signal to another process. If this signal is not caught (via the
signal system call) the process is terminated. There
is also an ``uncatchable'' signal. Exit is used for self termination
and can indicate success or failure.
Modern general purpose operating systems permit a user to create and
destroy processes.
Old or primitive operating system like
MS-DOS are not multiprogrammed so when one process starts another,
the first process is automatically blocked and waits until
the second is finished.
The diagram on the right contains much information.
The OS organizes the data about each process in a table naturally
called the process table.
Each entry in this table is called a
process table entry (PTE) or
process control block.
In a well defined location in memory (specified by the hardware) the
OS stores an interrupt vector, which contains the
address of the (first level) interrupt handler.
Assume a process P is running and a disk interrupt occurs for the
completion of a disk read previously issued by process Q, which is
currently blocked.
Note that interrupts are unlikely to be for the currently running
process (because the process waiting for the interrupt is likely
blocked).
The idea is to have separate threads of control (hence the name)
running in the same address space. Each thread is somewhat like a
process (e.g., it is scheduled to run) but contains less state (the
address space belongs to the process in which the thread runs.
A process contains a number of resources such as address space,
open files, accounting information, etc. In addition to these
resources, a process has a thread of control, e.g., program counter,
register contents, stack. The idea of threads is to permit multiple
threads of control to execute within one process. This is often
called multithreading and threads are often called
lightweight processes. Because the threads in a
process share so much state, switching between them is much less
expensive than switching between separate processes.
Individual threads within the same process are not completely
independent. For example there is no memory protection between them.
This is typically not a security problem as the threads are
cooperating and all are from the same user (indeed the same process).
However, the shared resources do make debugging harder. For example
one thread can easily overwrite data needed by another and if one thread
closes a file other threads can't read from it.
Often, when a process A is blocked (say for I/O) there is still
computation that can be done. Another process can't B do this
computation since it doesn't have access to the A's memory. But two
threads in the same process do share the memory so there is no problem.
An important example is a multithreaded web server. Each thread is
responding to a single WWW connection. While one thread is blocked on
I/O, another thread can be processing another WWW connection. Why not
use separate processes, i.e., what is the shared memory?
A common organization is to have a dispatcher thread that fields
requests and then passes this request on to an idle thread.
Another example is a producer-consumer problem
(c.f. below)
in which we have 3 threads in a pipeline.
One reads data, the second processes the data read, and the third
outputs the processed data. Again, while one thread is blocked the
others can execute.
Homework: 9.
Write a (threads) library that acts as a mini-scheduler and
implements thread_create, thread_exit,
thread_wait, thread_yield, etc. The central data
structure maintained and used by this library is the thread
table the analogue of the process table in the operating system
itself.
Advantages
Disadvantages
Move the thread operations into the operating system itself. This
naturally requires that the operating system itself be (significantly)
modified and is thus not a trivial undertaking.
One can write a (user-level) thread library even if the kernel also
has threads. Then each kernel thread can switch between user level
threads.The threads in a kernel-level thread system can
Skipped
The idea is to automatically issue a create thread system call upon
message arrival. (The alternative is to have a thread or process
blocked on a receive system call.)
If implemented well the latency between message arrival and thread
execution can be very small since the new thread does not have state
to restore.
Definitely NOT for the faint of heart.
A race condition occurs when two processes can
interact and the outcome depends on the order in which the processes
execute.
We must prevent interleaving sections of code that need to be atomic with
respect to each other. That is, the conflicting sections need
mutual exclusion. If process A is executing its
critical section, it excludes process B from executing its critical
section. Conversely if process B is executing is critical section, it
excludes process A from executing its critical section.
Requirements for a critical section implementation.
Ans: Because without init there would be no running process to create
any others.
2.1.3: Process Termination
2.1.4: Process Hierarchies
2.1.5: Process States and Transitions
A non-preemptive scheduler doesn't.
One can organize an OS around the scheduler.
2.1.6: Implementation of Processes
An aside on Interrupts (will be done again
here)
2.2: Threads
Per process items Per thread items
Address space Program counter
Global variables Machine registers
Open files Stack
Child processes
Pending alarms
Signals and signal handlers
Accounting information
2.2.1: The Thread Model
2.2.2: Thread Usage
Ans: The cache of frequently referenced pages.
2.2.3: Implementing threads in user space
2..2.4: Implementing Threads in the Kernel
2.2.5: Hybrid Implementations
2.2.6: Scheduler Activations
2.2.7: Popup Threads
Making Single-threaded Code Multithreaded
2.3: Interprocess Communication (IPC) and Process Coordination and
Synchronization
2.3.1: Race Conditions
Homework: 18.
2.3.2: Critical sections