6 Ipc
6 Ipc
2
The second issue is that two or more processes may get into
each other’s way when starting critical activities. Suppose
two processes each try to grab the last 1 MB of memory.
Two of these issues are valid for threads. The first one
passing information is easy for threads because they share a
common address space. For other two issues, same
problems exist and same solution can be applied to threads.
3
RACE CONDITIONS
In some operating systems, processes that are working
together may share some common storage that each one
can read and write. The shared storage may be in main
memory or it may be a shared file.
To see how interprocess communication works in
practice, let us consider a simple but common example: a
print spooler. When a process wants to print a file, it
enters the file name in a special spooler directory.
Another process, the printer daemon, periodically
checks to see if there are any files to be printed, and if
there are, it prints them and then removes their names
from the directory.
4
Imagine that our spooler directory has a very large number
of slots, numbered 0, 1, 2, …, each one capable of holding
a file name. Also imagine that there are two shared
variables, out, which points to the next file to be printed,
and in, which points to the next free slot in the directory.
5
Process A reads in and stores the value, 7, in a local variable
called next_free_slot. Just then a clock interrupt occurs and the
CPU switches to process B from Process A. Process B also
reads in, and also gets a 7. It stores it in its local variable
next_free_slot too. At this instant both processes think that the
next available slot is 7. 6
Process B now continues to run. It stores the name of its file in
slot 7 and updates in to be an 8.
Process A runs again, starting from the place where it left off.
It looks at next_free_slot, finds a 7 there, and writes its file
name in slot 7, erasing the name that process B just put there.
Then it computes next_free_slot + 1, which is 8, and sets in to
8. The spooler directory is now internally consistent, so the
printer daemon will not notice anything wrong, but process B
will never receive any output.
Situations like this, where two or more processes are reading
or writing some shared data and the final result depends on
who runs precisely when, are called race conditions.
7
CRITICAL REGIONS
Sometimes a process have to access shared memory or
files, or doing other critical things that can lead to races.
That part of the program which accesses the shared
memory or file is called the critical region or critical
section.
We need is mutual exclusion, that is, some way of
making sure that if one process is using a shared variable
or file, the other processes will be excluded from doing
the same thing.
Achieving mutual exclusion is a major design issue in
any operating system.
8
We need four conditions to hold to have a good solution
race conditions:
1. Any two processes can not be simultaneously inside their
critical regions.
2. No assumptions may be made about speeds or the number
of CPUs.
3. Any process running outside its critical region can not
block other processes.
4. Any process should not have to wait forever to enter its
critical region.
9
At time T1 process A enters its critical region
At T2 process B attempts to enter its critical region but fails. Until
T3 B is temporarily suspended.
At time T3, B enters its critical region
At T4, B leaves its critical region
10
MUTUAL EXCLUSION WITH BUSY WAITING
11
1.DISABLING INTERRUPTS
12
This approach is generally unattractive because it is not a
good idea to give user processes the power to turn off
interrupts.
Suppose that one of them did it and never turned it on
again? That could be the end of the system.
Furthermore if the system is a multiprocessor, with two or
more CPUs, disabling interrupts affects only the CPU that
executed the disable instruction. The other ones will
continue running and can access the shared memory.
It is frequently proper for the kernel itself to disable
interrupts for a few instructions while it is updating
variables or lists.
13
2. LOCK VARIABLES
This is a software solution. Consider having a single,
shared (lock) variable, initially 0. When a process wants to
enter its critical region, it first tests the lock. If the lock is 0,
the process sets it to 1 and enters the critical region. If the
lock is already 1, the process just waits until it becomes 0.
Thus, a 0 means that no process is in its critical region, and
a 1 means that a process is in its critical region.
This method also is not a good solution.Suppose that one
process reads the lock and sees that it is 0. Before it can set
the lock to 1, another process is scheduled, runs, and sets
the lock to 1. When the first process runs again, it will also
set the lock to 1, and two processes will be in their critical
regions at the same time. 14
3. STRICT ALTERNATION
An integer variable turn, initially 0, keeps track of whose
turn it is to enter the critical region and examine or
update the shared memory.
Process 1 Process 2
16
4.PETERSON’S SOLUTION
17
Before using the shared variables each process calls
enter_region with its own process number, 0 or 1, as
parameter. This call may cause it to wait until it is safe to
enter. After it has finished with the shared variables, the
process calls leave_region to indicate that it is done and to
allow the other process to enter.
Let us see how this solution works. Initially neither process
is in its critical region. Now process 0 calls enter_region. It
indicates its interest by setting its array element and sets turn
to 0. Since process 1 is not interested, enter_region returns
immediately. If process 1 now calls enter_region, it will
hang there until interested[0] goes to FALSE, an event that
only happens when process 0 calls leave_region to exit the
critical region.
18
Now consider the case that both processes call
enter_region almost simultaneously. Both will store their
process number in turn. Whichever is stored last falls in
loop; the first one is overwritten and lost. Suppose that
process 1 stores last, so turn is 1. When both processes
come to the while statement, process 0 executes it zero
times and enters its critical region. Process 1 loops and
does not enter its critical region until process 0 exits its
critical region.
19
5. THE TSL INSTRUCTION
Many computers, especially those designed with multiple
processors in mind, have an instruction:
TSL RX,LOCK
it is done.
When lock is 0, any process may set it to 1 using the TSL
instruction and then read or write the shared memory. When
it is done, the process sets lock back to 0 using an ordinary
move instruction.
21
SLEEP AND WAKEUP
Both Peterson’s solution and the solution using TSL are
correct, but both have the defect of requiring busy waiting.
When a process wants to enter its critical region, it checks
to see if the entry is allowed. If it is not allowed, the
process just sits in a tight loop waiting until it is allowed.
Beside of wasting CPU time, this approach can also have
unexpected effects. Consider a computer with two
processes, H, with high priority and L, with low priority.
The scheduling rules are such that H runs whenever it is in
ready state. At a certain moment, with L is in its critical
region, H becomes ready to run. H now begins busy
waiting. Before H is completed, L can not be scheduled. So
L never gets the chance to leave its critical region, so H 22
loops forever.
Now let me look at some interprocess communication
primitives that block processes when they are not allowed to
enter their critical regions, instead of wasting CPU time in
an empty loop.
23
THE PRODUCER-CONSUMER PROBLEM
24
Trouble arises when the producer wants to put a new
item in the buffer, but it is already full. The solution is
for the producer to go to sleep, and sleeping producer
will be awakened when the consumer has removed one
or more items. Similarly, if the consumer wants to
remove an item from the buffer and sees that the buffer
is empty, it goes to sleep until the producer puts
something in the buffer and wakes it up.
25
26
This approach sounds simple enough, but it leads to the
same kinds of race conditions we saw earlier with the
spooler directory. Race conditon can occur because access
to count is unconstrained.
28
SEMAPHORES
Semaphore is an integer variable to count the number of
wakeup processes saved for future use. A semaphore
could have the value 0, indicating that no wakeup
processes were saved, or some positive value if one or
more wakeups were pending.
There are two operations, down (wait) and up(signal)
(generalizations of sleep and wakeup, respectively). The
down operation on a semaphore checks if the value is
greater than 0. If so, it decrements the value and just
continues. If the value is 0, the process is put to sleep
without completing the down for the moment.
29
The up operation increments the value of the semaphore
addressed. If one or more processes were sleeping on
that semaphore, unable to complete an earlier down
operation, one of them is chosen by the system (e.g., at
random) and is allowed to complete its down.
30
SOLVING THE PRODUCER-CONSUMER
PROBLEM USING SEMAPHORES
Semaphores solve the lost-wakeup problem. It is
essential that they are implemented in an indivisible way.
The normal way is to implement up and down as system
calls, with the operating system briefly disabling all
interrupts while it is testing the semaphore, updating it,
and putting the process to sleep.
If multiple CPUs are being used, each semaphore should
be protected by a lock variable, with the TSL instruction
used to make sure that only one CPU at a time examines
the semaphore.
31
This solution uses three semaphores: one called full for
counting the number of slots that are full, one called empty
for counting the number of slots that are empty, and one
called mutex to make sure the producer and consumer do
not access the buffer at the same time. Full is initially 0,
empty is initially equal to the number of slots in the buffer
(N), and mutex is initially 1.
The mutex semaphore is used for mutual exclusion. It is
designed to guarantee that only one process at a time will
be reading or writing the buffer and the associated
variables. The other use of semaphores is for
synchronization. The full and empty semaphores are used
to guarantee synchronization. In this case, they ensure that
the producer stops running when the buffer is full, and the
consumer stops running when it is empty. 32
33
MUTEXES
Mutex is simplified version of the semaphore. When the
semaphore’s ability to count is not needed, mutex can be
used. Mutexes are good for managing mutual exclusion to
some shared resource or piece of code. They are easy and
efficient to implement.
A mutex is a variable that can be in one of two states:
unlocked or locked. Consequently, only 1 bit is required to
represent it, but in practice an integer often is used, with 0
meaning unlocked and all other values meaning locked.
34
Two procedures are used with mutexes. When a thread (or
process) needs access to a critical region, it calls
mutex_lock. If the mutex is current unlocked (meaning that
the critical region is available), the call succeeds and the
calling thread is free to enter the critical region.
35
Because mutexes are so simple, they can easily be
implemented in user space if a TSL instruction is
available
36
The code of mutex_lock is similar to the code of
enter_region of TSL Instruction but with a crucial
difference.
The difference is valid for threads. When enter_region fails
to enter the critical region, it keeps testing the lock
repeatedly (busy waiting). In mutex_lock, when the later
fails to acquire a lock, it calls thread_yield to give up the
CPU to another thread. Consequently there is no busy
waiting.
37
MESSAGE PASSING
Interprocess communication uses two primitives, send
and receive. They can easily be put into library
procedures, such as
send(destination, &message);
receive(source, &message);
The former call sends a message to a given destination
and the latter one receives a message from a given
source. If no message is available, the receiver can block
until one arrives. Alternatively, it can return immediately
with an error code.
38
Message passing systems have many problems, especially if
the communicating processes are on different machines
connected by a network. For example, messages can be lost
on the network. To solve this problem, as soon as a message
has been received, the receiver will send back a special
acknowledgement message. If the sender has not received
the acknowledgement within a certain time interval, it
retransmits the message.
Now consider what happens if the message itself is received
correctly, but the acknowledgement is lost. The sender will
retransmit the message, so the receiver will get it twice. It is
essential that the receiver is able to distinguish a new
message from the retransmission of an old one.
39
Usually, this problem is solved by putting consecutive
sequence numbers in each original message. If the receiver
gets a message bearing the same sequence number as the
previous message, it knows that the message is a duplicate
that can be ignored.
There are also design issues that are important when the
sender and receiver are on the same machine. One of these
is performance. Copying messages from one process to
another is always slower than doing a semaphore operation.
40
THE PRODUCER-CONSUMER
PROBLEM WITH MESSAGE PASSING
We assume that all messages are the same size and that
messages sent but not yet received are buffered
automatically by the operating system. In this solution, a
total of N messages are used, analogous to the N slots in a
shared memory buffer. The consumer starts out by sending N
empty messages to the producer. Whenever the producer has
an item to give to the consumer, it takes an empty message
and sends back a full one.
If the producer works faster than the consumer, all the
messages will be full, waiting for the consumer: the producer
will be blocked, waiting for an empty to come back. If the
consumer works faster, all the messages will be empty
waiting for the producer to fill them up: the consumer will 41
43
We see four processes approaching a barrier (Fig a). After a
while, the first process finishes all the computing during the
first phase. It then executes the barrier primitive, generally
by calling a library procedure. The process is then
suspended. A little later, a second and then a third process
finish the first phase (Fig b). Finally, when the last process,
C, hits the barrier, all the processes are released (Fig c)
44
ANY QUESTION?
45