Overview Of Process Continue... |
When build an event-driven system with several distinct serial activities,
threads are a key structuring mechanism of the OS.
A thread is again an execution stream in the context of a thread state. Key
difference between processes and threads is that multiple threads share parts of
their state. Typically, allow multiple threads to read and write same memory.
(Recall that no processes could directly access memory of another process). But,
each thread still has its own registers. Also has its own stack, but other
threads can read and write the stack memory.
What is in a thread control block? Typically just registers. Don't need to
do anything to the MMU when switch threads, because all threads can access same
memory.
Typically, an OS will have a separate thread for each distinct activity. In
particular, the OS will have a separate thread for each process, and that thread
will perform OS activities on behalf of the process. In this case we say that
each user process is backed by a kernel thread.
- When process issues a system call to read a file, the process's thread will take over, figure out which disk accesses to generate, and issue the low level instructions required to start the transfer.
It then suspends until the disk finishes reading in the data.
- When process starts up a remote TCP connection, its thread handles the low-level details of sending out network packets.
Having a separate thread for each activity allows the programmer to program
the actions associated with that activity as a single serial stream of actions
and events. Programmer does not have to deal with the complexity of interleaving
multiple activities on the same thread.
Why allow threads to access same memory? Because inside OS, threads must
coordinate their activities very closely.
- If two processes issue read file system calls at close to the same time, must make sure that the OS serializes the disk requests appropriately.
- When one process allocates memory, its thread must find some free memory and give it to the process. Must ensure that multiple threads allocate disjoint pieces of memory.
Having threads share the same address space makes it much easier to coordinate
activities - can build data structures that represent system state and have
threads read and write data structures to figure out what to do when they need
to process a request.
One complication that threads must deal with: asynchrony. Asynchronous
events happen arbitrarily as the thread is executing, and may interfere with the
thread's activities unless the programmer does something to limit the
asynchrony. Examples:
- An interrupt occurs, transferring control away from one thread
to an interrupt handler.
- A time-slice switch occurs, transferring control from one thread
to another.
- Two threads running on different processors read and write the
same memory.
Asynchronous events, if not properly controlled, can lead to incorrect
behavior. Examples:
- Two threads need to issue disk requests. First thread starts to
program disk controller (assume it is memory-mapped, and must issue
multiple writes to specify a disk operation). In the meantime, the
second thread runs on a different processor and also issues the
memory-mapped writes to program the disk controller. The disk
controller gets horribly confused and reads the wrong disk block.
- Two threads need to write to the display. The first thread
starts to build its request, but before it finishes a time-slice
switch occurs and the second thread starts its request. The
combination of the two threads issues a forbidden request sequence,
and smoke starts pouring out of the display.
- For accounting reasons the operating system keeps track of how
much time is spent in each user program. It also keeps a running sum
of the total amount of time spent in all user programs. Two threads
increment their local counters for their processes, then
concurrently increment the global counter. Their increments
interfere, and the recorded total time spent in all user processes
is less than the sum of the local times.
So, programmers need to coordinate the activities of the multiple threads so
that these bad things don't happen. Key mechanism: synchronization operations.
These operations allow threads to control the timing of their events relative to
events in other threads. Appropriate use allows programmers to avoid problems
like the ones outlined above.
|