- Concurrent programming introduces all sorts of interesting new
issues: How should the different processes share resources such as
the CPU and devices? How should they be laid out in memory? How
should processes be created and deleted? Where should their state
be stored when they are not running? What facilities should be
provided for cooperation and synchronization?
- The short-term scheduler selects from processes currently ready
to run. It runs on the order of every 100 milliseconds. The
medium-term scheduler is in charge of reducing the degree of
multiprogramming in the system when necessary, by swapping some
processes out. It runs only when necessary. The long-term
scheduler really only exists on batch systems: it selects processes
from a pool of jobs in mass storage and runs them one by one.
- A normal context switch requires the PC, the stack pointer, and
the registers to be reloaded with the new process' state. The
DECSYSTEM-20, which has multiple register sets, presumably needs
only the PC and the stack pointer to be updated (unless there are
separate PCs and SPs in each register set?) and for the active
register set to be changed. If all register sets are in use, then
one register set must be written to memory while the desired set is
copied in from memory.
- Threads have two advantages over multiple processes: they share
an address space, so communication is easier; and they are much
lighter weight and easier to create and destroy quickly. However,
if any thread makes a system call, then all user threads are stopped
while the request is serviced. A web server might benefit from the
use of threads so that it can handle many requests. A program to
find large prime numbers, which is very compute intensive and does
little I/O, might not.
- Threads require their own PC's, register sets, and stack.
Processes require all of those things plus their own address space,
open files, and signal handlers.
- To switch contexts between two threads, only the PC, the
stack pointer, and the registers need to be updated. Process
context switch requires all of those things, plus it requires
OS intervention for scheduling, placing the old process on the
ready (or waiting) queue and selecting the next one, and
possibly mapping some memory.
- User-level threads are managed by the user process and are
invisible to the kernel. However, they must either use
asynchronous I/O or all block if any thread needs to do I/O.
Kernel threads are managed by the OS. They take a lot longer
to switch between, but they don't suffer the I/O problems of
user threads.
- Producer:
repeat
...
produce an item in nextp
...
while (in = out) && (last_to_go = producer) do no-op;
buffer[in] := nextp;
last_to_go := producer;
in := in+1 mod n;
until false;
Consumer:
repeat
while (in = out) && (last_to_go = consumer) do no-op;
nextc := buffer[out];
last_to_go := consumer;
out := out+1 mod n;
...
consume the item in nextc
...
until false;
- Consider the mailbox IPC scheme.
a. The process should execute:
receive(A, message1);
receive(B, message2);
If it turns out that A takes a long time to come and B
comes right away, then it will be inefficient, but it will
still be correct.
b. The process might execute:
thread_fork(&Wait_for_message, A);
thread_fork(&Wait_for_message, B);
/* go to sleep until signaled */
Wait_for_message(Mailbox X) {
receive(X, message);
/* wake up main thread */
}
c. Not possible. What you really want is a "peek" operation
that tells you if there are any messages, but you don't
want to remove any messages you find (which the receive
operation will do) and you don't want to add any messages
that weren't there already (which the send operation will
do). Since both of the primitive operations (send and
receive) have adverse affects, there's no way to find out
the state of the box without perturbing the system.
- IPC, or interprocess communication, is handy when you
want multiple processes on the same machine to work
together. For instance, you may want to connect up some
processes with a pipe on the command line: "grep foobar
*.h | less". Or, in perhaps a more familiar example, you
may want to drag a piece of a spreadsheet in Excel into
your Word document. These are examples of IPC.
RPC, or remote procedure call, allows for a local process
to request that some work be done by a process on some
other machine. RPC can "make it look like" a remote
procedure call is just a regular procedure call, except
maybe not quite as reliable. Inside the guts of an RPC
call, the arguments to the remote procedure are
"marshalled," or bundled into a safe form for transmission
over a network, and sent as a message to some remote
process. The remote process (or the RPC handling layer
under it) unmarshalls them, executes the procedure, and
then sends back the result. This makes some client-server
operations much less painful. There may be more on this
if we make it to the distributed systems chapter.