Download 6. Process Synchronization

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

DNIX wikipedia , lookup

Distributed operating system wikipedia , lookup

Burroughs MCP wikipedia , lookup

Process management (computing) wikipedia , lookup

Transcript
Operating System Principles
AUA CIS215
Albert Minasyan
Handout 7
Process Synchronization
Content of this handout:
7.1. Cooperating Processes, Interprocess Communication

Producer-Consumer problem. Synchronization bounded, unbounded buffer problems.
7.2. Shared Memory Systems
7.3. Message passing systems.

Naming
o Direct (symmetric, asymmetric) communication, Indirect communication

Synchronization
o Rendezvous

Buffering

Process Synchronization

Semaphores
o Binary Semaphores. Bounded Buffer Problem, Counting Semaphores.

Deadlock
Silberschatz,6th ed, Chapters 4,7,8., Silberschatz,9th ed, Chapter 5, 7.
7.1. Cooperating Processes. Interprocess Communication.
The concurrent processes executing in the operating system may be either independent processes or
cooperating processes.
Processes running in the OS
Independent Processes


Cooperating Processes
Cannot affect or be affected by the
other processes.
Doesn’t share any data with any other
process.


Affects or is affected by the other
processes
Shares data with other processes.
A process is independent if it cannot affect or be affected by the other processes executing in the
system. Any process that does not share any data (temporary or persistent) with any other process is
independent.
On the other hand, a process is cooperating if it can affect or be affected by the other processes
executing in the system. Clearly, any process that shares data with other processes is a cooperating
process.




Process Cooperation Purposes
Information sharing (Share files)
Computation speedup (create subtasks to run simultaneously)
Modularity (system function divide into separate processes or threads)
Convenience (Run several tasks of the same user in the same environment)
Operating System Principles
AUA CIS215
Albert Minasyan
There are several reasons for providing an environment that allows process cooperation:




Information sharing: Since several users may be interested in the same piece of information (for
instance, a shared file), we must provide an environment to allow concurrent access to these types
of resources.
Computation speedup: If we want a particular task to run faster, we must break it into subtasks,
each of which will be executing in parallel with the others. Such a speedup can be achieved only
if the computer has multiple processing elements (such as CPUS or I/O channels).
Modularity: We may want to construct the system in a modular fashion, dividing the system
functions into separate processes or threads.
Convenience: Even an individual user may work on many tasks at the same time. For instance, a
user may be editing, listening to music, and compiling in parallel.
Concurrent execution of cooperating processes requires mechanisms that allow processes to
communicate with one another and to synchronize their actions.
Concurrent processes
Mechanisms to Communicate
Mechanisms to Synchronize
Shared Resource
Cooperating processes require an interprocess communication (IPC) mechanism that will allow them
to exchange data and information to communicate and to synchronize.
There are two fundamental models of interprocess communication:
(1) shared memory and
(2) message passing.
In the shared-memory model, a region of memory that is shared by cooperating processes is
established. Processes can then exchange information by reading and writing data to the shared
region.
In the message passing model, communication takes place by means of messages exchanged between
the cooperating processes. The two communications models are contrasted in Figure below.
Operating System Principles
AUA CIS215
Albert Minasyan
Figure: Communications models. (a) Message passing. (b) Shared memory.
Both of the models just discussed are common in operating systems, and many systems implement
both.
Message passing is useful for exchanging smaller amounts of data, because no conflicts need be
avoided. Message passing is also easier to implement than is shared memory for inter computer
communication.
Shared memory allows maximum speed and convenience of communication. Shared memory is
faster than message passing, as message passing systems are typically implemented using system
calls and thus require the more time-consuming task of kernel intervention. In contrast, in shared
memory systems, system calls are required only to establish shared-memory regions. Once shared
memory is established, all accesses are treated as routine memory accesses, and no assistance from the
kernel is required.
Message Passing
Shared Memory


Slow speed (kernel intervention)
Complex to implement for inter process
sharing.(data is sent by message).



Easier to implement for Inter Process and
Inter Computer communication /
synchronization

Maximum speed (memory operations)
Convenience for sharing between local
processes (simple write/read)
Difficult to implement for Inter Computer
sharing or communication (needs
polling).
Message Passing vs Shared Memory


Shared memory enjoys the desirable feature that all communications are done using implicit loads
and stores to a global address space.
Another fundamental feature of shared memory is that synchronization and communication are
distinct. Special synchronization operations (mechanisms), in addition to the loads and stores
operations, need to be employed in order to detect when data have been produced and/or
consumed.
Operating System Principles
AUA CIS215



Albert Minasyan
On the other hand, message passing employs an explicit communication model. Explicit messages
are exchanged among processors.
Synchronization and communication are unified in message passing. The generation of remote,
asynchronous events is an integral part of the message passing communication model.
It is important, however, to indicate that shared memory and message passing communication
models are universal; that is, it is possible to employ one to simulate the other.
o However, it is observed that it is easier to simulate shared memory using message passing
than the converse.
o This is basically because of the asynchronous event semantics of message passing as
compared to the polling semantics of the shared memory.
Producer – Consumer paradigm
To illustrate the concept of cooperating processes, let us consider the producer-consumer problem,
which is a common paradigm for cooperating processes.
A producer process produces information that is consumed by a consumer process. For example, a
print program produces characters that are consumed by the printer driver. A compiler may produce
assembly code, which is consumed by an assembler. The assembler, in turn, may produce object
modules, which are consumed by the loader.
The producer-consumer problem also provides a useful metaphor for the client-server paradigm. We
generally think of a server as a producer and a client as a consumer. For example, a Web server
produces (that is, provides) HTML files and images, which are consumed (that is, read) by the client
Web browser requesting the resource.
Producer – Consumer
common paradigm for cooperating processes
To run concurrently – we need a buffer
Producer
Printing
program
Consumer
Produce
Information
Consume
Information
Printer
driver
Buffer
To allow producer and consumer processes to run concurrently, we must have available a buffer of
items that can be filled by the producer and emptied by the consumer. This buffer will reside in a
region of memory that is shared by the producer and consumer processes. A producer can produce one
item while the consumer is consuming another item. The producer and consumer must be
synchronized, so that the consumer does not try to consume an item that has not yet been produced.
In this situation, the consumer must wait until an item is produced.
Operating System Principles
AUA CIS215
Albert Minasyan
Two types of buffers can be used.
The unbounded-buffer producer-consumer problem places no practical limit on the size of the
buffer. The consumer may have to wait for new items, but the producer can always produce new
items.
Synchronization: Unbounded buffer
Producer – Consumer example
Producer
Never waits
Consumer
Produce
Information
Consume
Information
Waits only if the
buffer is empty
Buffer
No limit on
the size
The bounded-buffer assumes a fixed buffer size. In this case, the consumer must wait if the buffer is
empty, and the producer must wait if the buffer is full.
The Bounded Buffer Problem has to do with two processes, the producer and the consumer,
who share a common, fixed-size buffer. The producer's job is to generate a piece of data, put it
into the buffer and start again. At the same time the consumer is consuming the data (i.e.
removing it from the buffer) one piece at a time. The problem is to make sure that the producer
won't try to add data into the buffer if it's full and that the consumer won't try to remove data from
an empty buffer.
Synchronization: Bounded buffer
Producer – Consumer example
Producer
Waits if the
buffer is full
Consumer
Produce
Information
Consume
Information
Waits if the
buffer is empty
Buffer
Limited
size
The buffer may either be provided by the operating system through the use of a message passing
system, or by explicitly coded by the application programmer with the use of shared memory.
Buffer is Provided:


Either by the OS through the use of Message Passing system
Or by explicitly coded application with the shared memory
Operating System Principles
AUA CIS215
Albert Minasyan
7.2. Shared Memory Systems
Interprocess communication using shared memory requires communicating processes to establish a
region of shared memory. Typically, a shared-memory region resides in the address space of the
process creating the shared memory segment. Other processes that wish to communicate using this
shared memory segment must attach it to their address space.
Recall that, normally, the operating system tries to prevent one process from accessing another
process's memory. Shared memory requires that two or more processes agree to remove this
restriction. They can then exchange information by reading and writing data in the shared areas.
The form of the data and the location are determined by these processes and are not under the
operating system's control. The processes are also responsible for ensuring that they are not writing to
the same location simultaneously.
Typically for using the shared memory the processes should:
•
•
•
•
•
•
•
Establish (allocate) a region of shared memory
Memory resides in the address space of the process creating the shared memory
segment
Other processes that wish to communicate using this shared memory segment must
attach it to their address space
Shared memory requires that two or more processes agree to remove the address space
protection restriction
They can then exchange information by reading and writing data in the shared areas
After finishing the transfer the memory should be detached.
Deallocate the shared memory.
One solution to the producer-consumer problem uses shared memory.
Let us illustrate a shared-memory solution to the bounded-buffer problem.
The producer and consumer processes share the following variables:
#define BUFFER-SIZE 5
typedef struct {
. . .
} item;
item buffer [BUFFER-SIZE] ;
int in = 0;
int out = 0;
in
out
pointers: in and out.
 in points to the next free position in the buffer;
 out points to the first full position in the buffer.
 The buffer is empty when in == out;

The buffer is full when ((in + 1) % BUFFER-SIZE) == out.
out
in
out
out
in
in
in
out
Operating System Principles
AUA CIS215
Albert Minasyan
The code for the producer and consumer processes follows.
The producer process has a local variable nextproduced in which the new item to be produced is
stored:
while (1) {
/* produce an item in nextproduced */
while ( ( (in + 1) % BUFFER-SIZE) == out)
; /* do nothing */
buffer [in] = nextproduced;
in = (in + 1) % BUFFER-SIZE;
}
The consumer process has a local variable nextconsumed in which the item to be consumed is
stored:
while (1) {
while (in == out)
; // do nothing
nextconsumed = buffer [out] ;
out = (out + 1) % BUFFER-SIZE;
/* consume the item in nextconsumed */
}
This scheme allows at most BUFFER-SIZE - 1 items in the buffer at the same time. We leave it as an
exercise for you to provide a solution where BUFFERSIZE items can be in the buffer at the same
time.
Operating System Principles
AUA CIS215
Albert Minasyan
7.3. Message Passing System
When the cooperating processes communicate in a shared-memory environment the scheme requires
that these processes share a common buffer pool, and that the code for implementing the buffer be
written explicitly by the application programmer. Another way to achieve the same effect is for the
operating system to provide the means for cooperating processes to communicate with each other via a
message-passing facility.
Message passing provides a mechanism to allow processes to communicate and to
synchronize their actions without sharing the same address space (buffer is provided by
OS).
Message passing is particularly useful in a distributed environment where the communicating
processes may reside on different computers connected with a network.
For example, a chat program used on the World Wide Web could be designed so that chat participants
communicate with one another by exchanging messages.
The function of a message system is to allow processes to communicate with one another without the
need to resort to shared data in the same address space.
In the scheme when the services are provided as ordinary user processes the services operate outside
of the kernel. Communication among the user processes is accomplished through the passing of
messages.
A Message passing facility provides at least the two operations: send(message) and
receive(message).
Messages sent by a process can be of either fixed or variable size. If only fixed-sized messages can be
sent, the system-level implementation is straightforward. This restriction, however, makes the task
of programming more difficult. On the other hand, variable-sized messages require a more complex
system-level implementation, but the programming task becomes simpler.
If processes P and Q want to communicate, they must send messages to and receive messages from
each other; a communication link must exist between them. This link can be implemented in a
variety of ways. We are concerned here not with the link's physical implementation (such as shared
memory, hardware bus, or network) but rather with its logical implementation. Here are several
methods for logically implementing a link and the send/receive operations:
If processes P and Q want to communicate
 a communication link (logical) must established between them
 they must send messages to and receive messages from each other.
Operating System Principles
AUA CIS215
Albert Minasyan
7.3.1. Naming
Processes that want to communicate must have a way to refer to each other. They can use
either direct or indirect communication.
7.3.1.1. Direct communication
With direct communication, each process that wants to communicate must explicitly name the
recipient or sender of the communication. In this scheme, the send and receive primitives are
defined as:
send (P , message) - Send a message to process P.
receive (Q , message) - Receive a message from process Q.
A communication link in this scheme has the following properties:
 A link is established automatically between every pair of processes that want to communicate.
The processes need to know only each other's identity to communicate.
 A link is associated with exactly two processes.
 Exactly one link exists between each pair of processes.
This scheme exhibits symmetry in addressing; that is, both the sender and the receiver processes must
name the other to communicate.
Direct Communication
Processes must name each other explicitly:
send (P, message) – send a message to process P
receive(Q, message) – receive a message from process Q
Properties of communication link
Links are established automatically
A link is associated with exactly one pair of communicating processes
Between each pair there exists exactly one link
The link may be unidirectional, but is usually bi-directional
Direct Symmetric Communication
Process P
Process Q
Send to Q
message
Receive from
P message
Process R
Send to R
message
Receive from Q
message
OS Buffer
Receive from Q
message
OS Buffer
Send to P
message
Receive from R
message
OS Buffer
OS Buffer
Link QR
Link PQ
Send to Q
message
Operating System Principles
AUA CIS215
Albert Minasyan
A variant of this scheme employs asymmetry in addressing. Only the sender names the recipient; the
recipient is not required to name the sender.
In this scheme, the send and receive primitives are defined as follows:
send (P , message) - Send a message to process P.
receive (id, message) -Receive a message from any process; the variable id is set to
the name of the process with which the communication has taken place.
Direct Asymmetric Communication
Process P
Process Q
Process R
Receive from X
message
X=R or X=Q
Is set by OS
Send to Q
message
OS Buffer
Send to Q
message
OS Buffer
Disadvantage: Direct communication is tightly related with the names of processes
and changing the names cause changes in programs.
The disadvantage in both symmetric and asymmetric schemes is the limited modularity of the
resulting process definitions. Changing the name of a process may necessitate examining all other
process definitions. All references to the old name must be found, so that they can be modified to the
new name. This situation is not desirable from the viewpoint of separate compilation.
Operating System Principles
AUA CIS215
Albert Minasyan
7.3.1.2. Indirect communication
With indirect communication, the messages are sent to and received from mailboxes, or ports. A
mailbox can be viewed abstractly as an object into which messages can be placed by processes and
from which messages can be removed. Each mailbox has a unique identification. In this scheme, a
process can communicate with some other process via a number of different mailboxes.
The operating system must provide a mechanism that allows a process to do the following:
 Create a new mailbox.
 Send and receive messages through the mailbox.
 Delete a mailbox.
Two processes can communicate only if they share a mailbox. The send and receive primitives
are defined as follows:
send (A, message) -Send a message to mailbox A.
receive (A, message) -Receive a message from mailbox A.
In this scheme, a communication link has the following properties:
Indirect Communication
Processes can communicate only if they share a mailbox or port (with unique identification):
send (A, message) – send a message to mailbox A
receive (A, message) – receive a message from mailbox A
Properties of communication link
 A link is established between a pair of processes only if both members of the pair have a
shared mailbox.
 A link may be associated with more than two processes.
 A number of different links may exist between each pair of communicating processes,
with each link corresponding to one mailbox.
 The link may be unidirectional or bi-directional

Indirect Communication
Process P
Process Q
Receive from
PQ1 message
Process R
Send to PQ1
message
PQ mailbox 1
Send to PQR1
message
Receive from
PQR1 message
PQ mailbox 2
Send to PQ2
message
Send to PQR1
message
User sends
message
Receive from
PQ2 message
User sends
message
PQR mailbox 1
Owner receives
message
Operating System Principles
AUA CIS215


Albert Minasyan
Mailbox owner could be the process (mailbox attached to it).
 Mailbox could be created deleted with the process.
 Ownership could be transferred to the other process.
Mailbox owner could be OS. The process will request
 creation or deletion of mailbox
 Send and receive messages through the mailbox
Now suppose that processes P1, P2, and P3 all share mailbox A. Process P1 sends a
message to A, while P2 and P3 each execute a receive from A.
Which process will receive the message sent by P1 ?
The answer depends on the scheme that we choose:
 Allow a link to be associated with at most two processes.
 Allow at most one process at a time to execute a receive operation.
 Allow the system to select arbitrarily which process will receive the message (that is, either
P2 or P3, but not both, will receive the message). The system may identify the receiver to
the sender.
A mailbox may be owned either by a process or by the operating system. If the mailbox is owned by a
process (that is, the mailbox is part of the address space of the process), then we distinguish between
the owner (who can only receive messages through this mailbox) and the user (who can only send
messages to the mailbox). Since each mailbox has a unique owner, there can be no confusion about
who should receive a message sent to this mailbox. When a process that owns a mailbox terminates,
the mailbox disappears. Any process that subsequently sends a message to this mailbox must be
notified that the mailbox no longer exists.
On the other hand, a mailbox owned by the operating system is independent and is not attached to any
particular process. The operating system then must provide a mechanism that allows a process to do
the following:
 Create a new mailbox.
 Send and receive messages through the mailbox.
 Delete a mailbox.
The process that creates a new mailbox is that mailbox's owner by default. Initially, the owner is the
only process that can receive messages through this mailbox. However, the ownership and receive
privilege may be passed to other processes through appropriate system calls. Of course, this provision
could result in multiple receivers for each mailbox.
Operating System Principles
AUA CIS215
Albert Minasyan
7.3.2. Message Synchronization
Communication between processes takes place by calls to send and receive primitives. There
are different design options for implementing each primitive.
Message passing may be either blocking or nonblocking-also known as synchronous and
asynchronous.
 Blocking send: The sending process is blocked until the message is received by the receiving
process or by the mailbox.
 Nonblocking send: The sending process sends the message and resumes operation.
 Blocking receive: The receiver blocks until a message is available.
 Nonblocking receive: The receiver retrieves either a valid message or a null.
Different combinations of send and receive are possible. When both the send and receive are
blocking, we have a rendezvous between the sender and the receiver.
Process 1
Process 2
Non blocking receive (0)
To the mailbox or to the process
Non blocking receive
(message)
Non blocking send
Blocking receive
Waiting message
Blocking send
Waiting until message reaches recipient
Non blocking send
Rendezvous
answering
7.3.3. Message Buffering
Whether the communication is direct or indirect, messages exchanged by communicating processes
reside in a temporary queue. Basically, such a queue can be implemented in three ways:

Zero capacity: The queue has maximum length 0; thus, the link cannot have any messages
waiting in it. In this case, the sender must block until the recipient receives the message.
 Bounded capacity: The queue has finite length n; thus, at most n messages can reside in it. If the
queue is not full when a new message is sent, the latter is placed in the queue (either the message
is copied or a pointer to the message is kept), and the sender can continue execution without
waiting. The link has a finite capacity, however. If the link is full, the sender must block until
space is available in the queue.
 Unbounded capacity: The queue has potentially infinite length; thus, any number of messages
can wait in it. The sender never blocks.
The zero-capacity case is sometimes referred to as a message system with no buffering; the other
cases are referred to as automatic buffering.
Operating System Principles
AUA CIS215
Albert Minasyan
7.3.4. Process Synchronization
Synchronization involves the orderly sharing of system resources by
processes. To illustrate, consider the following intersection diagram to
the right. We can think of this intersection as a system resource that is
shared by two processes: the car process and the train process. If only
one process is active, then no resource conflict exists. But what happens
when both processes are active and they both arrive at the intersection
simultaneously? In this case, the shared resource becomes a problem.
They cannot both use the resource at the same time or a collision will
occur. Similarly, processes sharing resources on a computer must be
properly managed in order to avoid "collisions."
The illustration of this concept with the example of a shared printer:
"Consider a machine with a single printer running a time-sharing operation
system. If a process needs to print its results, it must request that the
operating system give it access to the printer's device driver. At this
point, the operating system must decide whether to grant this request,
depending upon whether the printer is already being used by another
process. If it is not, the operating system should grant the request and
allow the process to continue; otherwise, the operating system should deny
the request and perhaps classify the process as a waiting process until the
printer becomes available. Indeed, if two processes were given
simultaneous access to the machine's printer, the results would be worthless
to both."
Incorrect Value
Unsynchronized Threads Sharing Memory
Operating System Principles
AUA CIS215
Albert Minasyan
The figure above shows what can happen when two unsynchronized threads share a resource such
as a memory location. Both threads increment variable N, but, because of the particular sequence in
which the threads might execute, the final value of N is 5, whereas the correct value is 6. Notice that
the particular result shown here is neither repeatable nor predictable; a different thread execution
sequence could yield the correct results. Execution on an SMP system can aggravate this problem.
Now that the problem of synchronization is properly stated, consider the following related definitions
 Critical Resource: a resource shared with constraints on its use (e.g., memory, files, printers, etc.)
 Critical Section: code that accesses a critical resource
 Mutual Exclusion: at most one process may be executing a Critical Section with respect to a
particular critical resource simultaneously
Using CRITICAL_SECTIONs is simple, and one common use to allow threads to access global
shared variables or resources.
Critical Resource
Dear OS here the critical
section starts. Please run
it with MUTEX.
Dear OS here the critical
section starts. Please run
it with MUTEX.
Dear OS here the critical
section ends. Please run
the other critical sections
if they blocked.
Correct Value
Synchronized Threads Sharing Memory
Dear OS here the critical
section ends. Please run
the other critical sections
if they blocked.
Let’s return to the printer example. In the above example the printer is the critical resource. Let's
suppose that the processes which are sharing this resource are called process A and process B. The
critical sections of process A and process B are the sections of the code which issue the print
command.
Operating System Principles
AUA CIS215
Albert Minasyan
7.3.5. Semaphores
In order to insure that both processes do not attempt to use the printer at the
same time, they must be granted mutually exclusive access to the printer
driver. We can illustrate the idea of mutual exclusion with our railroad
intersection by adding a semaphore to the picture.
Semaphores are used in software systems in much the same way as they
are in railway systems.
Corresponding to the section of track that can contain only one train at a
time is a sequence of instructions that can be executed by only one process
at a time. Such a sequence of instructions is called a critical [section].
One way to implement semaphores in computers is to use a flag (i.e., a bit of memory) that can be
tested and set to either 0 or 1 in a single machine operation. Because both the test and set actions
occur in the same machine instruction, this operation is indivisible and cannot be interrupted by
another process which is running on the machine. By placing test-and-set operations around the
critical section of a program, programmers can guarantee mutually exclusive access to the critical
resource.
The mutual exclusion of critical sections ensures that the Test&Set section is executed atomically that is, as one uninterruptible unit.
Operating System Principles
AUA CIS215
Albert Minasyan
If the critical section is too long then this approach with Atomic Test & Set for Mutex is an active
waiting thread which consumes the CPU cycles.
When the thread waits and while is waiting consumes the CPU cycles then it is called Busy Waiting
and this is a disadvantage of this approach.
Calls to either acquire() or release() must be performed atomically. Thus, mutex locks are often
implemented using one of the hardware mechanisms
The main disadvantage of the implementation given here is that it requires busy waiting. While a
process is in its critical section, any other process that tries to enter its critical section must loop
continuously in the call to acquire().
In fact, this type of mutex lock is also called a spinlock because the process “spins” while waiting for
the lock to become available. This continual looping is clearly a problem in a real multiprogramming
system, where a single CPU is shared among many processes. Busy waiting wastes CPU cycles that
some other process might be able to use productively.
Spinlocks do have an advantage, however, in that no context switch is required when a process must
wait on a lock, and a context switch may take considerable time. Thus, when locks are expected to be
held for short times, spinlocks are useful. They are often employed on multiprocessor systems where
one thread can “spin” on one processor while another thread performs its critical section on another
processor.
Operating System Principles
AUA CIS215
Albert Minasyan
Let’s represent the above flowcharts by functions to use them in more comfortable way and also
dedicate 2 different threads for a producer and a consumer.
Semaphores suggest to sleep the process in waiting queue when it waits for the lock acquiring.
In this case there is need in more variables than one Mutex but the main problem with the busy
waiting is resolved.
Mutex locks, as we mentioned earlier, are generally considered the simplest of synchronization tools.
In this section, we examine a more robust tool that can behave similarly to a mutex lock but can also
provide more sophisticated ways for processes to synchronize their activities.
A semaphore S is an integer variable that, apart from initialization, is accessed only through two
standard atomic operations: wait() and signal().
The wait() operation was originally termed P (from the Dutch proberen, “to test”);
signal() was originally called V (from verhogen, “to increment”).
All modifications to the integer value of the semaphore in the wait() and signal() operations must be
executed indivisibly. That is, when one process modifies the semaphore value, no other process can
simultaneously modify that same semaphore value. In addition, in the case of wait(S), the testing of
the integer value of S (S ≤ 0), as well as its possible modification (S--), must be executed without
interruption.
Semaphore Usage
Operating systems often distinguish between counting and binary semaphores.
The value of a counting semaphore can range over an unrestricted domain.
The value of a binary semaphore can range only between 0 and 1.
Thus, binary semaphores behave similarly to mutex locks. In fact, on systems that do not provide
mutex locks, binary semaphores can be used instead for providing mutual exclusion.
Counting semaphores can be used to control access to a given resource consisting of a finite number
of instances. The semaphore is initialized to the number of resources available. Each process that
wishes to use a resource performs a wait() operation on the semaphore (thereby decrementing the
count). When a process releases a resource, it performs a signal() operation (incrementing the count).
Operating System Principles
AUA CIS215
Albert Minasyan
When the count for the semaphore goes to 0, all resources are being used. After that, processes that
wish to use a resource will block until the count becomes greater than 0.
We can also use semaphores to solve various synchronization problems.
Mutex Semaphore usage for producer and consumer:
Producer(item) {
Lock_Acquire();
Enqueue(item);
Lock_Release();
}
Consumer() {
Lock_Acquire();
item = Dequeue();
Lock_Release();
return item;
}
7.3.5.1. Mutex or Binary Semaphore Example
The pictures below illustrate three processes sharing one critical resource. In order to access the
critical section of their code, the processes must wait until the semaphore Mutex (Mutual exclusion)
is one. When this happens, the process uses the test-and-set operation to change the value of Mutex to
zero and then it executes its critical section. Upon exiting the critical section, the process set the
value of Mutex back to one to allow other processes to access the critical resource. The critical
section is represented by a light color.
http://courses.cs.vt.edu/~csonline/OS/Lessons/
Operating System Principles
AUA CIS215
Albert Minasyan
Another way to implement a semaphore is to use a count rather than just two values. Such semaphores
are called counting semaphores in contrast to the binary semaphore presented above. Counting
semaphores are used to solve synchronization problems such as the Bounded Buffer problem.
7.3.5.2. Bounded Buffer Problem. Counting Semaphore Example
Suppose a system incorporates two processes, one of which produces information (the producer
process) and another process that uses the information (the consumer process). The two processes
communicate by having the producer obtain an empty buffer from an empty buffer pool, fill it with
information, and place it in a pool of full buffers. The consumer obtains information by picking up a
buffer from the full buffer pool, copying the information out of the buffer, and placing it in the empty
buffer pool for recycling. The producer and consumer use a fixed, finite number, N, of buffers to pass
an arbitrary amount of information between them.
The producer and consumer processes must be synchronized so that the consumer process blocks
(i.e., pauses its execution) whenever all the buffers are empty, and the producer process blocks
whenever all the buffers are full. To enforce this synchronization, counting semaphores are used to
count the number of empty and full buffers.
The picture below illustrates the Bounded Buffer problem. In this example, the size of the buffer pool
is five (N = 5) and the data being shared are characters. The producer process fills one buffer with a
character each time it executes, and the consumer process removes one character from the buffer each
time it executes.
Dark pointer points the
empty buffer where the
producer next should fill
the buffer. Light pointer
shows the full buffer where
from the consumer should
take the information and
empty it.
Operating System Principles
AUA CIS215
Albert Minasyan
7.3.6. Deadlock
Silberschatz 9th ed. Chapter 7.
In a multiprogramming environment, several processes may compete for a finite number of resources.
A process requests resources; if the resources are not available at that time, the process enters a
waiting state.
Sometimes, a waiting process is never again able to change state, because the resources it
has requested are held by other waiting processes. This situation is called a deadlock.
In a deadlock, processes never finish executing, and system resources are tied up,
preventing other jobs from starting.
Although some applications can identify programs that may deadlock, operating systems typically do
not provide deadlock-prevention facilities, and it remains the responsibility of programmers to ensure
that they design deadlock-free programs. Deadlock problems can only become more common, given
current trends, including larger numbers of processes, multithreaded programs, many more resources
within a system, and an emphasis on long-lived file and database servers rather than batch systems.
Consider the following situation: Suppose that two processes (A and B) are running on the same
machine, and both processes require the use of the local printer and tape drive. Process A may have
been granted access to the machine's printer but be waiting for the tape drive, while process B has
access to the tape drive but is waiting for the printer. Such a condition is known as deadlock. Since
both processes hold a resource the other process needs, the processes will wait indefinitely for the
resources to be released and neither will finish executing.
Thread 1
Thread 2
Got Printer access
Holding Printer
Waiting
Disk access
Got Disk access
Holding Disk
Waiting
Printer access
In the example given above, the printer and tape drive represent mutually exclusive resources. Since
these resources cannot be space-multiplexed, processes using them are granted complete control of the
resources until they are finished.
Operating System Principles
AUA CIS215
Albert Minasyan
System Model
A system consists of a finite number of resources to be distributed among a number of competing
processes.
The resources may be partitioned into several types (or classes), each consisting of some number of
identical instances. CPU cycles, files, and I/O devices (such as printers and DVD drives) are examples
of resource types. If a system has two CPUs, then the resource type CPU has two instances. Similarly,
the resource type printer may have five instances. If a process requests an instance of a resource type,
the allocation of any instance of the type should satisfy the request. If it does not, then the instances
are not identical, and the resource type classes have not been defined properly. For example, a system
may have two printers. These two printers may be defined to be in the same resource class if no one
cares which printer prints which output. However, if one printer is on the ninth floor and the other is
in the basement, then people on the ninth floor may not see both printers as equivalent, and separate
resource classes may need to be defined for each printer.
A process must request a resource before using it and must release the resource after using it. A
process may request as many resources as it requires to carry out its designated task. Obviously, the
number of resources requested may not exceed the total number of resources available in the system.
In other words, a process cannot request three printers if the system has only two.
Under the normal mode of operation, a process may utilize a resource in only the following sequence:
1. Request. The process requests the resource. If the request cannot be granted immediately (for
example, if the resource is being used by another process), then the requesting process must wait until
it can acquire the resource.
2. Use. The process can operate on the resource (for example, if the resource is a printer, the process
can print on the printer).
3. Release. The process releases the resource.
A deadlock situation can arise if the following four conditions hold simultaneously in a system:
1. Mutual exclusion. At least one resource must be held in a nonsharable mode; that is, only one
process at a time can use the resource. If another process requests that resource, the requesting process
must be delayed until the resource has been released.
2. Hold and wait. A process must be holding at least one resource and waiting to acquire additional
resources that are currently being held by other processes.
3. No preemption. Resources cannot be preempted; that is, a resource can be released only
voluntarily by the process holding it, after that process has completed its task.
4. Circular wait. A set {P0, P1, ..., Pn} of waiting processes must exist such that P0 is waiting for a
resource held by P1, P1 is waiting for a resource held by P2, ..., Pn−1 is waiting for a resource held by
Pn, and Pn is waiting for a resource held by P0.
We emphasize that all four conditions must hold for a deadlock to occur. The circular-wait condition
implies the hold-and-wait condition, so the four conditions are not completely independent.
If the operating system allows process A and process B to hold-and-wait, and it does not forcibly
remove any resources from these processes, then deadlock is possible. Note, however, that the four
conditions do not guarantee deadlock. They are necessary, but not sufficient, for the occurrence of
deadlock.
Operating System Principles
AUA CIS215
Albert Minasyan
Resource-Allocation Graph
Deadlocks can be described more precisely in terms of a directed graph called a system resourceallocation graph. This graph consists of a set of vertices V and a set of edges E. The set of vertices
V is partitioned into two different types of nodes: P = {P1, P2, ..., Pn}, the set consisting of all the
active processes in the system, and R = {R1, R2, ..., Rm}, the set consisting of all resource types in the
system.
A directed edge from process Pi to resource type Rj is denoted by Pi → Rj ;
it signifies that process Pi has requested an instance of resource type Rj and is currently waiting for
that resource.
A directed edge from resource type Rj to process Pi is denoted by Rj → Pi ; it signifies that an instance
of resource type Rj has been allocated to process Pi .
A directed edge Pi → Rj is called a request edge;
a directed edge Rj → Pi is called an assignment edge.
Pictorially, we represent each process Pi as a circle and each resource type Rj as a rectangle. Since
resource type Rj may have more than one instance, we represent each such instance as a dot within the
rectangle. Note that a request edge points to only the rectangle Rj , whereas an assignment edge must
also designate one of the dots in the rectangle.
When process Pi requests an instance of resource type Rj , a request edge is inserted in the resourceallocation graph. When this request can be fulfilled, the request edge is instantaneously
transformed to an assignment edge. When the process no longer needs access to the resource, it
releases the resource. As a result, the assignment edge is deleted.
The resource-allocation graph shown in Figure 7.1 depicts the following
situation.
Figure 7.1 Resource-allocation graph.
• The sets P, R, and E:
◦ P = {P1, P2, P3}
◦ R = {R1, R2, R3, R4}
◦ E = {P1 → R1, P2 → R3, R1 → P2, R2 → P2, R2 → P1, R3 → P3}
Operating System Principles
AUA CIS215
Albert Minasyan
• Resource instances:
◦ One instance of resource type R1
◦ Two instances of resource type R2
◦ One instance of resource type R3
◦ Three instances of resource type R4
• Process states:
◦ Process P1 is holding an instance of resource type R2 and is waiting for an instance of
resource type R1.
◦ Process P2 is holding an instance of R1 and an instance of R2 and is waiting for an instance
of R3.
◦ Process P3 is holding an instance of R3.
Given the definition of a resource-allocation graph, it can be shown that, if the graph contains no
cycles, then no process in the system is deadlocked. If the graph does contain a cycle, then a deadlock
may exist.
If each resource type has exactly one instance, then a cycle implies that a deadlock has occurred.
If the cycle involves only a set of resource types, each of which has only a single instance, then a
deadlock has occurred. Each process involved in the cycle is deadlocked. In this case, a cycle in the
graph is both a necessary and a sufficient condition for the existence of deadlock.
If each resource type has several instances, then a cycle does not necessarily imply that a deadlock has
occurred. In this case, a cycle in the graph is a necessary but not a sufficient condition for the
existence of deadlock.
To illustrate this concept, we return to the resource-allocation graph depicted in Figure 7.1. Suppose
that process P3 requests an instance of resource
type R2. Since no resource instance is currently available,we add a request edge
P3→ R2 to the graph (Figure 7.2). At this point, two minimal cycles exist in the
system:
P1 → R1 → P2 → R3 → P3 → R2 → P1
P2 → R3 → P3 → R2 → P2
Processes P1, P2, and P3 are deadlocked. Process P2 is waiting for the resource
R3, which is held by process P3. Process P3 is waiting for either process P1 or
process P2 to release resource R2. In addition, process P1 is waiting for process
P2 to release resource R1.
Operating System Principles
AUA CIS215
Albert Minasyan
Figure 7.2 Resource-allocation graph with a deadlock.
Now consider the resource-allocation graph in Figure 7.3. In this example, we also have a cycle:
P1 → R1 → P3 → R2 → P1
Figure 7.3 Resource-allocation graph with a cycle but no deadlock.
However, there is no deadlock. Observe that process P4 may release its instance of resource type R2.
That resource can then be allocated to P3, breaking the cycle. In summary, if a resource-allocation
graph does not have a cycle, then the system is not in a deadlocked state. If there is a cycle, then the
system may or may not be in a deadlocked state. This observation is important when we deal with the
deadlock problem.
Operating System Principles
AUA CIS215
Albert Minasyan
Methods for Handling Deadlocks
Generally speaking, we can deal with the deadlock problem in one of three ways:
• We can use a protocol to prevent or avoid deadlocks, ensuring that the system will never enter a
deadlocked state.
• We can allow the system to enter a deadlocked state, detect it, and recover.
• We can ignore the problem altogether and pretend that deadlocks never occur in the system.
One solution to this problem is to allow deadlock to occur, detect it, and then correct the problem.
Usually this correction involves forcibly deallocating resources from deadlocked processes. Notice
that this solution to deadlock involves removing the third condition, that is, the operation system now
allows for preemption of resources whenever deadlock occurs.
Another solution to the problem of deadlock is to remove the second condition by requiring
processes to request all of their resources at the same time.
Yet another solution is to remove the first condition by converting nonshareable resources into
shareable ones.
Suppose the resource in question is a printer and a variety of processes require its use. Each time a
process requests the printer, the operating system grants the request. However, instead of connecting
the process to the printer's device driver, the operating system connects it to a device driver that stores
the information to be printed on a disk rather than sending it to the printer. Thus each process,
thinking it has access to the printer, executes in its normal way. Later, when the printer is available,
the operating system can transfer the data from the disk to the printer. In this manner the operating
system has made the nonshareable resource appear shareable by creating the illusion of more than one
printer.
Deadlock example
A famous illustration of the problem of deadlock was given by the Edgar Dijkstra in 1968. The
problem, known as the Dining Philosophers, is illustrated in the picture below.
Five philosophers sit around a circular table. Each philosopher spends his life alternatively thinking
and eating. In the center of the table is a large plate of spaghetti. A philosopher needs two forks to eat
the spaghetti. Unfortunately the philosophers can only afford five forks. One fork is placed between
each pair of philosophers and they agree that each will only use the fork to his immediate right and
left.
When a philosopher thinks, she does not interact with her colleagues. From time to time, a philosopher
gets hungry and tries to pick up the two chopsticks that are closest to her (the chopsticks that are
between her and her left and right neighbors). A philosopher may pick up only one chopstick at a
time. Obviously, she cannot pick up a chopstick that is already in the hand of a neighbor. When a
hungry philosopher has both her chopsticks at the same time, she eats without releasing her
chopsticks. When she is finished eating, she puts down both of her chopsticks and starts thinking
again.
Operating System Principles
AUA CIS215
Albert Minasyan
It’s possible that each of the philosophers becomes hungry and grabs a single fork. Since no more
forks are available and two forks are required for eating, the philosophers all wait for another fork and
deadlock occurs.
Note that the Dining Philosophers problem meets all three of the conditions presented earlier.



Only one philosopher can use a fork at a time so the fork resource is mutually exclusive.
Hungry philosophers with only one fork will hold this resource until another fork becomes
available, thus resulting in hold-and-wait.
And since the philosophers are peaceful and courteous, no one is willing to forcibly remove a
fork from his neighbor.
Several possible remedies to the deadlock problem are listed next.
 Allow at most four philosophers to be sitting simultaneously at the table.
 Allow a philosopher to pick up her chopsticks only if both chopsticks are available (to do this she
must pick them up in a critical section).
 Use an asymmetric solution; that is, an odd philosopher picks up first her left chopstick and then
her right chopstick, whereas an even philosopher picks up her right chopstick and then her left
chopstick. To avoid the possibility of deadlock even numbered philosophers should pick up the
forks in a different order from the rest. That is, left first rather than right first. Notice that this
solution differs from the other approaches presented. Rather than attacking one of the necessary
conditions for deadlock, the solution imposes a particular resource allocation order which makes
it impossible for all five philosophers to be holding a single fork.
Finally, any satisfactory solution to the dining-philosophers problem must guard against the possibility
that one of the philosophers will starve to death. A deadlock-free solution does not necessarily
eliminate the possibility of starvation.
Operating System Principles
AUA CIS215
Albert Minasyan
POSIX Semaphores
DESCRIPTION
This manual page documents POSIX 1003.1b semaphores, not
to be confused with SystemV semaphores as described in
ipc(5), semctl(2) and semop(2).
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
- sem_init initializes the semaphore
object pointed to by sem.
int sem_wait(sem_t * sem);
int sem_trywait(sem_t * sem);
int sem_post(sem_t * sem);
(suspends thread until sem is non 0,
then atomically decreases the sem count)
- non-blocking variant of sem_wait
- atomically increases the count
of the semaphore pointed to by sem
int sem_getvalue(sem_t * sem, int * sval); - stores in the location
pointed to by sval the current
count of the semaphore sem.
int sem_destroy(sem_t * sem);
- destroys
a
semaphore
object
Operating System Principles
AUA CIS215
Albert Minasyan
WinAPI Semaphores
case WM_CREATE:
hSema= CreateSemaphore (NULL, 1, 1, "mysem");
. . .
- global variable
case WM_LBUTTONDOWN:
CreateThread (NULL,0,
(LPTHREAD_START_ROUTINE) MyThread1,(LPVOID)hwnd, 0, &Tid1);
CreateThread (NULL,0,
(LPTHREAD_START_ROUTINE) MyThread2,(LPVOID)hwnd, 0, &Tid2);
. . .
//-----------------------------------------------------------------DWORD MyThread1 (LPVOID param)
{
if (WaitForSingleObject (hSema, 10000) == WAIT_TIMEOUT)
{ MessageBox ((HWND) param, "Thread 1 - Timeout", "Semaphore error",
MB_OK);
return 0;
}
for (i=0; i<10; i++)
{
. . . }
ReleaseSemaphore (hSema, 1, NULL);
return 0;
}
DWORD MyThread2 (LPVOID param)
{
if (WaitForSingleObject (hSema, 10000) == WAIT_TIMEOUT)
{ MessageBox ((HWND)param, "Thread 2 - Timeout", "Semaphore error",
MB_OK);
return 0;
}
for (i=0; i<10; i++)
{ . . . }
ReleaseSemaphore (hSema, 1, NULL);
return 0;
}