0% found this document useful (0 votes)
34 views38 pages

Os Unit 3

Uploaded by

rakeshreddyt25
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views38 pages

Os Unit 3

Uploaded by

rakeshreddyt25
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

UNIT-III

Deadlocks
Definition
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.

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.
 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. request and release of resources may be
done through system calls, like request() and release() for device, open() and close() for
file, and allocate() and free() for memory wait() and signal() for semaphores acquire()
and release() for mutex lock.
 For each use of a kernel-managed resource by a process or thread, the operating system
checks to make sure that the process has requested and has been allocated the resource. A
system table records whether each resource is free or allocated. For each resource that is
allocated, the table also records the process to which it is allocated. If a process requests
a resource that is currently allocated to another process, it can be added to a queue of
processes waiting for this resource.

Illustration of a deadlocked state


1. Using same resource
Consider a system with three CD RW drives. Suppose each of three processes holds
one of these CD RW drives. If each process now requests another drive, the three processes
will be in a deadlocked state. Each is waiting for the event “CD RW is released,” which can
be caused only by one of the other waiting processes.
2. Different resource types
Consider a system with one printer and one DVD drive. Suppose that process Pi is
holding the DVD and process Pj is holding the printer. If Pi requests the printer and Pj
requests the DVD drive, a deadlock occurs.
Deadlock Characterization
1
In a deadlock, processes never finish executing, and system resources are tied up,
preventing other jobs from starting.
The following are the features that characterize deadlocks,
1. Necessary Conditions
A deadlock situation can arise if the following four conditions hold simultaneously in a
system:
a. Mutual exclusion: At least one resource must be held in a non-sharable 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.
b. 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.
c. No pre-emption: 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.
d. 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.

2. Resource-Allocation Graph
 Deadlocks can be in terms of a directed graph called a system resource-allocation
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 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, each instance or use of Ri with dot.
 Example:
 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}
 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.

2
Note: 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.
Resource Allocation Graph With a Deadlock
 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 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.

Resource Allocation Graph With a Cycle but No Deadlock


 Now consider the resource-allocation graph we also have a cycle: P1 → R1 → P3 →
R2 → P1
 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.

Methods for Handling Deadlocks


3
We can deal with the deadlock problem in one of three ways:
1. We can use a protocol to prevent or avoid deadlocks, ensuring that the system will
never enter a deadlocked state.
 To ensure that deadlocks never occur, the system can use either deadlock prevention
or a deadlock-avoidance scheme.
 Deadlock prevention provides a set of methods to ensure that at least one of the
necessary conditions cannot hold. These methods prevent deadlocks by constraining
how requests for resources can be made.
 Deadlock avoidance requires that the operating system be given additional
information in advance concerning which resources a process will request and use
during its lifetime. With this additional knowledge, the operating system can decide
for each request whether or not the process should wait.

2. We can allow the system to enter a deadlocked state, detect it, and recover.
 If a system does not employ either a deadlock-prevention or a deadlock avoidance
algorithm, then a deadlock situation may arise. In this environment, the system can
provide an algorithm that examines the state of the system to determine whether a
deadlock has occurred and an algorithm to recover from the deadlock (if a deadlock
has indeed occurred).

3. We can ignore the problem altogether and pretend that deadlocks never occur in
the system.
 In the absence of algorithms to detect and recover from deadlocks, the system will
be in a deadlocked state which cause the system’s performance to deteriorate.

Deadlock Prevention
For a deadlock to occur, each of the four necessary conditions must hold. By ensuring
that at least one of these conditions cannot hold, we can prevent the occurrence of a deadlock.
1. Mutual Exclusion
 The mutual exclusion condition must hold at least for non sharable resource like
printer, Sharable resources like Read-only files, in contrast, do not require mutually
exclusive access and thus cannot be involved in a deadlock.
 If several processes attempt to open a read-only file at the same time, they can be
granted simultaneous access to the file. A process never needs to wait for a sharable
resource. In general, however, we cannot prevent deadlocks by denying the mutual-
exclusion condition.

2. Hold and Wait


 To ensure that the hold-and-wait condition never occurs in the system, we must
guarantee that, whenever a process requests a resource, it does not hold any other
resources.
 To achieve this we have 2 protocols:
 Each process to request and be allocated all its resources before it begins
execution.
4
For example, we consider a process that copies data from a DVD drive
to a file on disk, sorts the file, and then prints the results to a printer. If all
resources must be requested at the beginning of the process, then the process
must initially request the DVD drive, disk file, and printer. It will hold the
printer for its entire execution, even though it needs the printer only at the end.
 Allows a process to request resources only when it has none. A process may
request some resources and use them. Before it can request any additional
resources, it must release all the resources that it is currently allocated.
For example, method allows the process to request initially only the
DVD drive and disk file. It copies from the DVD drive to the disk and then
releases both the DVD drive and the disk file. The process must then request the
disk file and the printer. After copying the disk file to the printer, it releases
these two resources and terminates.
 Disadvantages
Both these protocols have two main disadvantages.
 Resource Utilization may be low, since resources may be allocated but unused
for a long period (in first case).
 Starvation is possible. A process that needs several popular resources may have
to wait indefinitely, because at least one of the resources that it needs is always
allocated to some other process.

3. No Preemption
The third necessary condition for deadlocks is that there is no pre-emption of
resources that have already been allocated. To ensure that this condition does not hold, we
can use the following protocol,
 If a process is holding some resources and requests another resource that cannot be
immediately allocated to it (that is, the process must wait), then all resources the
process is currently holding are preempted. In other words, these resources are
implicitly released. The preempted resources are added to the list of resources for
which the process is waiting. The process will be restarted only when it can regain
its old resources, as well as the new ones that it is requesting.
 If a process requests some resources, we first check whether they are available. If
they are, we allocate them. If they are not, we check whether they are allocated to
some other process that is waiting for additional resources. If so, we preempt the
desired resources from the waiting process and allocate them to the requesting
process. If the resources are neither available nor held by a waiting process, the
requesting process must wait. While it is waiting, some of its resources may be
preempted, but only if another process requests them. A process can be restarted
only when it is allocated the new resources it is requesting and recovers any
resources that were pre-empted while it was waiting.

4. Circular Wait
 One way to ensure that this condition never holds is to impose a total ordering of all
resource types and to require that each process requests resources in an increasing

5
order of enumeration.
 Let R = {R1, R2, Rm} be the set of resource types. We assign to each resource type a
unique integer number, which allows us to compare two resources and to determine
whether one precedes another in our ordering.
 We define a one-to-one function F: R→N, where N is the set of natural numbers. For
example, if the set of resource types R includes tape drives, disk drives
F might be defined as follows:
F (tape drive) = 1
F (disk drive) = 5
F(printer) = 12
 We can now consider the following protocol to prevent deadlocks:
 Each process can request resources only in an increasing order of enumeration.
The process can request instances of resource type Rj if and only if F (Rj) > F
(Ri).
For example: a process that wants to use the tape drive and printer at
the same time must first request the tape drive and then request the printer.
 Alternatively, we can require that a process requesting an instance of resource
type Rj must have released any resources Ri such that F (Ri) ≥ F (Rj).
 Witness
Although ensuring that resources are acquired in the proper order is the
responsibility of application developers, certain software can be used to verify that
locks are acquired in the proper order and to give appropriate warnings when locks
are acquired out of order and deadlock is possible. One lock-order verifier, which
works on BSD versions of UNIX such as FreeBSD, is known as Witness.

Deadlock Avoidance
Avoiding deadlocks require additional information about how resources are to be
requested. For example, in a system with one tape drive and one printer, the system might
need to know that process P will request first the tape drive and then the printer before
releasing both resources, whereas process Q will request first the printer and then the tape
drive.
With this knowledge of the complete sequence of requests and releases for each
process, the system can decide for each request whether or not the process should wait in
order to avoid a possible future deadlock.
1. Safe State
 A state is safe if the system can allocate resources in safe sequence. A sequence of
processes is a safe sequence for the current allocation state if, for each Pi , the
resource requests that Pi can still make can be satisfied by the currently available
resources plus the resources held by all Pj, with j < i.
 A safe state is not a deadlocked state. An unsafe state may lead to a deadlock.

6
 Example:
 Consider a system with twelve magnetic tape drives and three processes: P0, P1,
and P2. Process P0 requires ten tape drives, process P1 may need as many as four
tape drives, and process P2 may need up to nine tape drives. Suppose that, at time
t0, process P0 is holding five tape drives, process P1 is holding two tape drives,
and process P2 is holding two tape drives.

 At time t0, the system is in a safe state. The sequence satisfies the safety
condition. Process P1 can immediately be allocated all its tape drives and then
return them (the system will then have five available tape drives); then process P0
can get all its tape drives and return them (the system will then have ten available
tape drives); and finally process P2 can get all its tape drives and return them (the
system will then have all twelve tape drives available).
 A system can go from a safe state to an unsafe state. Suppose that, at time t1,
process P2 requests and is allocated one more tape drive. The system is no longer
in a safe state. At this point, only process P1 can be allocated all its tape drives.
When it returns them, the system will have only four available tape drives. Since
process P0 is allocated five tape drives but has a maximum of ten, it may request
five more tape drives. If it does so, it will have to wait, because they are
unavailable. Similarly, process P2 may request six additional tape drives and have
to wait, resulting in a deadlock.
 To define avoidance algorithms that ensure that the system will never deadlock
we have to ensure that the system will always remain in a safe state.
 There are two deadlock avoidance algorithms,
1. Resource-Allocation-Graph Algorithm.
2. Banker’s Algorithm.

2. Resource-Allocation-Graph Algorithm
 We can use a variant of the resource-allocation graph. In addition to the request and
assignment edges already described, we introduce a new type of edge, called a claim
edge. A claim edge Pi → Rj indicates that process Pi may request resource Rj at some
time in the future. This edge resembles a request edge in direction but is represented
in the graph by a dashed line.

7
 When process Pi requests resource Rj , the claim edge Pi → Rj is converted to a
request edge. Similarly, when a resource Rj is released by Pi , the assignment edge Rj
→ Pi is reconverted to a claim edge Pi → Rj .

 Suppose that process Pi requests resource Rj . The request can be granted only if
converting
the request edge Pi → Rj to an assignment edge Rj → Pi does not result in the
formation of a cycle in the resource-allocation graph. If a cycle is found, process Pi
will have to wait for its requests to be satisfied.

3. Banker’s Algorithm
 The resource-allocation-graph algorithm is not applicable to a resource allocation
system with multiple instances of each resource type. So we move on to Banker’s
Algorithm. The name was chosen because the algorithm could be used in a banking
system to ensure that the bank never allocated its available cash in such a way that it
could no longer satisfy the needs of all its customers. But this is less efficient than
Resource-Allocation-Graph Algorithm.
 Data Structures
We need the following data structures, where n is the number of processes in the
system and m is the number of resource types:
 Available. A vector of length m indicates the number of available resources of
each type. If Available[j] equals k, then k instances of resource type Rj are
available.
 Max. An n × m matrix defines the maximum demand of each process. If
Max[i][j] equals k, then process Pi may request at most k instances of resource
type Rj .
 Allocation. An n × m matrix defines the number of resources of each type
currently allocated to each process. If Allocation[i][j] equals k, then process Pi is
currently allocated k instances of resource type Rj .
 Need. An n × m matrix indicates the remaining resource need of each process. If
Need[i][j] equals k, then process Pi may need k more instances of resource type
Rj to complete its task. Note that Need[i][j] equals Max[i][j] − Allocation[i][j].
a. Safety Algorithm
This finds out whether or not a system is in a safe state.
1. Let Work and Finish be vectors of length m and n, respectively. Initialize Work =
Available and Finish[i] = false for i = 0, 1, ..., n − 1.

8
2. Find an index i such that both
a. Finish[i] == false
b. Needi ≤ Work
If no such i exists, go to step 4.
3. Work = Work + Allocationi
Finish[i] = true Go to step 2.
4. If Finish[i] == true for all i, then the system is in a safe state

b. Resource-Request Algorithm
This is used for determining whether requests can be safely granted.
Let Requesti be the request vector for process Pi . If Requesti [j] == k, then process Pi
wants k instances of resource type R j . When a request for resources is made by process P i ,
the following actions are taken:
1. If Requesti ≤ Needi , go to step 2. Otherwise, raise an error condition, since the process
has exceeded its maximum claim.
2. If Requesti ≤ Available, go to step 3. Otherwise, Pi must wait, since the resources are not
available.
3. Have the system pretend to have allocated the requested resources to process Pi by
modifying the state as follows:
Available = Available–Requesti ;
Allocationi = Allocationi + Requesti ;
Needi = Needi –Requesti ;
If the resulting resource-allocation state is safe, the transaction is completed, and
process Pi is allocated its resources. However, if the new state is unsafe, then Pi must wait for
Requesti , and the old resource-allocation state is restored.

c. Example
Consider a system with five processes P0 through P4 and three resource types A, B,
and C. Resource type A has ten instances, resource type B has five instances, and resource
type C has seven instances. Suppose that, at time T0, the following snapshot of the system
has been taken:

The content of the matrix Need is defined to be Max − Allocation and is as follows:

9
We claim that the system is currently in a safe state. Indeed, the sequence satisfies
the safety criteria. Suppose now that process P1 requests one additional instance of resource
type A and two instances of resource type C, so Request1 = (1,0,2). To decide whether this
request can be immediately granted, we first check that Request1 ≤ Available—that is, that
(1,0,2) ≤ (3,3,2), which is true. We then pretend that this request has been fulfilled, and we
arrive at the following new state:

We must determine whether this new system state is safe. To do so, we execute
our safety algorithm and find that the sequence satisfies the safety requirement. Hence, we
can immediately grant the request of process P1.
You should be able to see, however, that when the system is in this state, a
request for (3,3,0) by P4 cannot be granted, since the resources are not available.
Furthermore, a request for (0,2,0) by P0 cannot be granted, even though the resources are
available, since the resulting state is unsafe.

Deadlock Detection
If a system does not employ either a deadlock-prevention or a deadlock avoidance
algorithm, then a deadlock situation may occur. In this environment, the system may provide:
 An algorithm that examines the state of the system to determine whether a deadlock has
occurred.
 An algorithm to recover from the deadlock.

1. Single Instance of Each Resource Type


 In this, we use a variant of the resource-allocation graph, called a wait-for graph. We
obtain this graph from the resource-allocation graph by removing the resource nodes
and collapsing the appropriate edges.
 An edge from Pi to Pj in a wait-for graph implies that process Pi is waiting for process
Pj to release a resource that Pi needs. An edge Pi → Pj exists in a wait-for graph if
and only if the corresponding resource allocation graph contains two edges Pi → Rq
and Rq → Pj for some resource Rq.
 A deadlock exists in the system if and only if the wait-for graph contains a cycle. To
10
detect deadlocks, the system needs to maintain the waitfor graph and periodically
invoke an algorithm that searches for a cycle in the graph.

2. Several Instances of a Resource Type


 The wait-for graph scheme is not applicable to a resource-allocation system with
multiple instances of each resource type. So we apply a detection algorithm similar to
the banker’s algorithm
 Data Structures
 Available. A vector of length m indicates the number of available resources of
each type.
 Allocation. An n × m matrix defines the number of resources of each type
currently allocated to each process.
 Request. An n × m matrix indicates the current request of each process. If
Request[i][j] equals k, then process Pi is requesting k more instances of resource
type Rj .

Detection Algorithm
The detection algorithm described here simply investigates every possible allocation
sequence for the processes that remain to be completed.
1. Let Work and Finish be vectors of length m and n, respectively. Initialize Work =
Available. For i = 0, 1, ..., n–1, if Allocationi ≠ 0, then Finish[i] = false. Otherwise,
Finish[i] = true.
2. Find an index i such that both
a. Finish[i] == false
b. Requesti ≤ Work If no such i exists, go to step 4.
3. Work = Work + Allocationi
Finish[i] = true Go to step 2.
4. If Finish[i] == false for some i, 0 ≤ i < n, then the system is in a deadlocked state.
Moreover, if Finish[i] == false, then process Pi is deadlocked.

Example:
We consider a system with five processes P0 through P4 and three resource types
A, B, and C. Resource type A has seven instances, resource type B has two instances, and
resource type C has six instances. Suppose that, at time T0, we have the following resource-
allocation state:

11
We claim that the system is not in a deadlocked state. Indeed, if we execute our
algorithm, we will find that the sequence results in Finish[i] == true for all i. Suppose now
that process P2 makes one additional request for an instance of type C. The Request matrix is
modified as follows:

We claim that the system is now deadlocked. Although we can reclaim the resources
held by process P0, the number of available resources is not sufficient to fulfill the requests
of the other processes. Thus, a deadlock exists, consisting of processes P1, P2, P3, and P4.

Detection-Algorithm Usage
When should we invoke the detection algorithm? The answer depends on two factors:
1. How often is a deadlock likely to occur?
2. How many processes will be affected by deadlock when it happens?
If deadlocks occur frequently, then the detection algorithm should be invoked
frequently. Resources allocated to deadlocked processes will be idle until the deadlock can be
broken. In addition, the number of processes involved in the deadlock cycle may grow

Recovery from Deadlock


When a detection algorithm determines that a deadlock exists, several alternatives are
available.
 One possibility is to inform the operator that a deadlock has occurred and to let the
operator deal with the deadlock manually.
 Another possibility is to let the system recover from the deadlock automatically.

There are two options for breaking a deadlock.


1. One is simply to abort one or more processes to break the circular wait.
2. The other is to preempt some resources from one or more of the deadlocked
processes.

1. Process Termination
To eliminate deadlocks by aborting a process, we use one of two methods.
 Abort all deadlocked processes. This method clearly will break the deadlock cycle,
12
but at great expense. The deadlocked processes may have computed for a long time,
and the results of these partial computations must be discarded and probably will have
to be recomputed later.
 Abort one process at a time until the deadlock cycle is eliminated. This method
incurs considerable overhead, since after each process is aborted, a deadlock-detection
algorithm must be invoked to determine whether any processes are still deadlocked.

Aborting a process may not be easy. If the process was in the midst of
updating a file, terminating it will leave that file in an incorrect state. Similarly, if the
process was in the midst of printing data on a printer, the system must reset the printer
to a correct state before printing the next job. If the partial termination method is used,
then we must determine which deadlocked process (or processes) should be
terminated.
Many factors may affect which process is chosen, including:
1. What the priority of the process is?
2. How long the process has computed and how much longer the process will
compute before completing its designated task?
3. How many and what types of resources the process has used (for example,
whether the resources are simple to preempt) ?
4. How many more resources the process needs in order to complete?
5. How many processes will need to be terminated?
6. Whether the process is interactive or batch?

2. Resource Preemption
If preemption is required to deal with deadlocks, then three issues need to be
addressed:
a. Selecting a victim.
Which resources and which processes are to be preempted? As in process
termination, we must determine the order of preemption to minimize cost. Cost
factors may include such parameters as the number of resources a deadlocked process
is holding and the amount of time the process has thus far consumed.
b. Rollback.
If we preempt a resource from a process, what should be done with that
process? Clearly, it cannot continue with its normal execution; it is missing some
needed resource. We must roll back the process to some safe state and restart it from
that state.
c. Starvation
How do we ensure that starvation will not occur? That is, how can we
guarantee that resources will not always be preempted from the same process? We
must ensure that a process can be picked as a victim only a (small) finite number of
times. The most common solution is to include the number of rollbacks in the cost
factor.

13
PROCESS MANAGEMENT AND SYNCHRONIZATION
Race Condition
Definition: A situation where several processes access and manipulate the same data
concurrently and the outcome of the execution depends on the particular order in which the
access takes place, is called a race condition.
To guard against the race condition we need to ensure that only one process at a time
can be manipulating the variable counter. To make such a guarantee, we require that the
processes be synchronized in some way.

The Critical Section Problem


Consider a system consisting of n processes {P0, P1, ..., Pn−1}. Each process has a
segment of code, called a critical section or region, in which the process may be changing
common variables, updating a table, writing a file, and so on.
The important feature of the system is that, when one process is executing in its
critical section, no other process is allowed to execute in its critical section. That is, no two
processes are executing in their critical sections at the same time.
The critical-section problem is to design a protocol that the processes can use to
cooperate. Each process must request permission to enter its critical section. The section of
code implementing this request is the entry section. The critical section may be followed by
an exit section. The remaining code is the remainder section.
A solution to the critical-section problem must satisfy the following three
requirements:
1. Mutual exclusion. If process Pi is executing in its critical section, then no other
processes can be executing in their critical sections.
2. Progress. If no process is executing in its critical section and some processes wish to
enter their critical sections, then only those processes that are not executing in their
remainder sections can participate in deciding which will enter its critical section next,
and this selection cannot be postponed indefinitely.
3. Bounded waiting. There exists a bound, or limit, on the number of times that other
processes are allowed to enter their critical sections after a process has made a request to
enter its critical section and before that request is granted.
General structure of process Pi

Synchronization Hardware
Software-based solutions such as Peterson’s does not guaranteed to work on modern
computer architectures. The following are several more solutions to the critical-section

10
problem using techniques ranging from hardware to software-based on the premise of
locking —that is, protecting critical regions through the use of locks.

Single Processor Systems: Disabling of Interrupts


The critical-section problem could be solved simply in a single-processor
environment if we could prevent interrupts from occurring while a shared variable was being
modified. In this way, we could be sure that the current sequence of instructions would be
allowed to execute in order without preemption.
No other instructions would be run, so no unexpected modifications could be made to
the shared variable. This is often the approach taken by nonpreemptive kernels.

Disable Interrupts
Critical section
Enable Interrupts
Remainder section

Multi Processor Systems: Special hardware instructions


Disabling interrupts on a multiprocessor can be time consuming, since the message is
passed to all the processors. This message passing delays entry into each critical section, and
system efficiency decreases.
Many modern computer systems therefore provide special hardware instructions like
“test _and _set” and “Compare _and_Swap” that allow us either to test and modify the
content of a word or to swap the contents of two words atomically—that is, as one
uninterruptible unit.
1. Test _and _Set
 The important characteristic of this instruction is that it is executed atomically.
 Thus, if two test and set () instructions are executed simultaneously (each on a
different CPU), they will be executed sequentially in some arbitrary order.
 If the machine supports the test and set () instruction, then we can implement mutual
exclusion by declaring a boolean variable lock, initialized to false.
Definition of the test_and _set() instruction:
boolean test_and_set (boolean *target)
{
boolean rv = *target;
*target = TRUE;
return rv:
}
Mutual-exclusion implementation with test _and _set ()
do
{
while (test_and_set (&lock))
; /* do nothing */
/* critical section */
lock = false;
/* remainder section */
11
} while (true);

2. Compare _and_Swap
 The compare and swap () instruction, in contrast to the test and set () instruction,
operates on three operands.
 The operand value is set to new value only if the expression (*value == expected) is
true. Regardless, compare and swap () always returns the original value of the
variable value.
Definition of compare _and_swap
int compare _and_swap(int *value, int expected, int new_value)
{
int temp = *value;
if (*value == expected)
*value = new_value;
return temp;
}
Mutual-exclusion implementation with compare _and_swap
do
{
while (compare_and_swap(&lock, 0, 1) != 0)
; /* do nothing */
/* critical section */
lock = 0;
/* remainder section */
} while (true);

Although these algorithms satisfy the mutual-exclusion requirement, they do not


satisfy the bounded-waiting requirement. The following is another algorithm using the test
and set () instruction that satisfies all the critical-section requirements.
do
{
waiting[i]=true;
key=true;
while (waiting[i] && key)
key = test_and_set (&lock);
waiting[i] = false;
/* critical section */
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i)
lock = false;
else
waiting[j] = false;

12
/* remainder section */
} while (true);

Semaphores
Hardware based solutions to critical section problems are complicated, so we use a
more robust software tool called Semaphore.
Definition: A semaphore S is an integer variable that, apart from initialization, is accessed
only through two standard atomic operations: wait () and signal ().
Definition of the wait () operation

Wait(S)
{
while (S <= 0)
; // busy wait
S--;
}
Definition of the signal () operation
Signal(S)
{
S++;
}

1. Semaphore Usage
Operating systems often distinguish between counting and binary semaphores.
a. Counting Semaphore
 The value of a counting semaphore can range over an unrestricted domain.
 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). 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.
b. Binary Semaphore
 The value of a binary semaphore can range only between 0 and 1.
 Example: Consider two concurrently running processes: P1 with a statement S1
and P2 with a statement S2. Suppose we require that S2 be executed only after S1
has completed. We can implement this scheme readily by letting P1 and P2 share
a common semaphore synch, initialized to 0.
In process P1, we insert the statements
S1;
signal (synch);
In process P2, we insert the statements
13
wait (synch);
S2;
Because synch is initialized to 0, P2 will execute S2 only after P1 has invoked signal
(synch), which is after statement S1 has been executed.

2. Semaphore Implementation
The previous definition of wait () and signal () has a problem of busy waiting which
wastes CPU cycles. To overcome this, we will modify the definition of wait () and signal ().
Wait ()
 When a process executes the wait () operation and finds that the semaphore value is not
positive, it must wait.
 However, rather than engaging in busy waiting, the process can block itself.
 The block operation places a process into a waiting queue associated with the
semaphore, and the state of the process is switched to the waiting state.
 Then control is transferred to the CPU scheduler, which selects another process to
execute.

Signal ()
 A process that is blocked, waiting on a semaphore S, should be restarted when some
other process executes a signal () operation.
 The process is restarted by a wakeup () operation, which changes the process from the
waiting state to the ready state.
 The process is then placed in the ready queue.
Defining a semaphore
typedef struct
{
int value;
struct process *list;
} semaphore;

wait () semaphore operation can be defined as,


wait (semaphore *S)
{
S->value--;
if(S->value<0)
{
add this process to S->list;
block ();
}
}

signal () semaphore operation can be defined as,


signal (semaphore *S)
{
S->value++;
14
if(S->value<=0)
{
remove a process P from S->list;
wakeup (P);
}
}

3. Deadlocks and Starvation


The implementation of a semaphore with a waiting queue may result in a situation
where two or more processes are waiting indefinitely for an event that can be caused only by
one of the waiting processes. The event in question is the execution of a signal () operation.
When such a state is reached, these processes are said to be deadlocked.
Example: consider a system consisting of two processes, P0 and P1, each accessing two
semaphores, S and Q, set to the value 1:

Suppose that P0 executes wait(S) and then P1 executes wait (Q).When P0 executes
wait (Q), it must wait until P1 executes signal (Q). Similarly, when P1 executes wait(S), it
must wait until P0 executes signal(S). Since these signal () operations cannot be executed, P0
and P1 are deadlocked.
Another problem related to deadlocks is indefinite blocking or starvation, a situation
in which processes wait indefinitely within the semaphore. Indefinite blocking may occur if
we remove processes from the list associated with a semaphore in LIFO (last-in, first-out)
order.

Classic Problems of Synchronization


1. The Bounded-Buffer Problem
The producer and consumer processes share the following data structures:
int n;
semaphore mutex = 1;
semaphore empty = n;
semaphore full = 0
We assume that the pool consists of n buffers, each capable of holding one item. The
mutex semaphore provides mutual exclusion for accesses to the buffer pool and is initialized
to the value 1. The empty and full semaphores count the number of empty and full buffers.
The semaphore empty is initialized to the value n; the semaphore full is initialized to the
value 0.
The structure of the producer process
do
{
...
/* produce an item in next_produced */
15
...
wait (empty);
wait (mutex);
...
/* add next produced to the buffer */
...
signal (mutex);
signal (full);
} while (true);

The structure of the consumer process


do
{
wait (full);
wait (mutex);
...
/* remove an item from buffer to next_consumed */
...
signal (mutex);
signal (empty);
...
/* consume the item in next consumed */
...
} while (true);

2. The Readers–Writers Problem


Suppose that a database is to be shared among several concurrent processes.
Some of these processes may want only to read the database, whereas others may want to
update (that is, to read and write) the database. We distinguish between these two types
of processes by referring to the former as readers and to the latter as writers. Obviously,
if two readers access the shared data simultaneously, no adverse effects will result.
However, if a writer and some other process (either a reader or a writer) access the
database simultaneously, problems may ensue.
To ensure that these difficulties do not arise, we require that the writers have
exclusive access to the shared database while writing to the database. This
synchronization problem is referred to as the readers–writers problem.

Variations in readers–writers problem


The readers–writers problem has several variations, all involving priorities.
 The simplest one, referred to as the first readers–writers problem, requires that no reader
be kept waiting unless a writer has already obtained permission to use the shared object.
In other words, no reader should wait for other readers to finish simply because a writer
is waiting. In this writers may starve.

16
 The second readers –writer’s problem requires that, once a writer is ready, that writer
perform its write as soon as possible. In other words, if a writer is waiting to access the
object, no new readers may start reading. In this readers may starve.

Solution to the first readers–writers problem

Data structures:
semaphore rw mutex = 1;
semaphore mutex = 1;
int read count = 0;
The semaphores mutex and rw mutex are initialized to 1; read count is initialized to 0.
The semaphore rw mutex is common to both reader and writer processes. The mutex
semaphore is used to ensure mutual exclusion when the variable read count is updated. The
read count variable keeps track of how many processes are currently reading the object. The
semaphore rw mutex functions as a mutual exclusion semaphore for the writers. It is also
used by the first or last reader that enters or exits the critical section. It is not used by readers
who enter or exit while other readers are in their critical sections.
The structure of a writer process
do
{
wait (rw_mutex);
...
/* writing is performed */
...
signal(rw_mutex);
} while (true);

The structure of a reader process


do
{
wait(mutex);
read_count++;
if (read_count == 1)
wait(rw_mutex);
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
readcount--;
if (read_count == 0)
signal(rw_mutex);
signal(mutex);
} while (true);

17
Reader–Writer Locks
The readers–writers problem and its solutions have been generalized to provide
reader–writer locks on some systems. Acquiring a reader–writer lock requires specifying the
mode of the lock: either read or write access. When a process wishes only to read shared
data, it requests the reader–writer lock in read mode. A process wishing to modify the shared
data must request the lock in write mode. Multiple processes are permitted to concurrently
acquire a reader–writer lock in read mode, but only one process may acquire the lock for
writing, as exclusive access is required for writers.

Reader–writer locks are most useful in the following situations:


 In applications where it is easy to identify which processes only read shared data and
which processes only write shared data.
 In applications that have more readers than writers.

3. The Dining-Philosophers Problem


Consider five philosophers who spend their lives thinking and eating. The
philosophers share a circular table surrounded by five chairs, each belonging to one
philosopher. In the centre of the table is a bowl of rice, and the table is laid with five single
chopsticks. 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 neighbours). 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 neighbour. When a hungry philosopher has both her chopsticks at the
same time, she eats without releasing the chopsticks. When she is finished eating, she puts
down both chopsticks and starts thinking again.

Solution
One simple solution is to represent each chopstick with a semaphore. A philosopher
tries to grab a chopstick by executing a wait () operation on that semaphore. She releases her
chopsticks by executing the signal () operation on the appropriate semaphores. Thus, the
shared data are
semaphore chopstick [5];
where all the elements of chopstick are initialized to 1.
The structure of Philosopher i:
do
{
wait (chopstick[i]);
wait (chopStick [(i + 1) % 5]);

18
// eat for a while
signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5] );

// think for a while


} while (TRUE);

Although this solution guarantees that no two neighbours are eating simultaneously, it
nevertheless must be rejected because it could create a deadlock. Suppose that all five
philosophers become hungry at the same time and each grabs her left chopstick. All the
elements of chopstick will now be equal to 0. When each philosopher tries to grab her right
chopstick, she will be delayed forever.
Several possible remedies to the deadlock problem are replaced by:
 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-numbered philosopher picks up first her
left chopstick and then her right chopstick, whereas an even numbered philosopher
picks up her right chopstick and then her left chopstick.

Monitors
Although semaphores provide a convenient and effective mechanism for process
synchronization, using them incorrectly can result in errors such as following,
 Suppose that a process interchanges the order in which the wait() and signal() operations
on the semaphore mutex are executed, resulting in the following execution:
signal (mutex);
...
critical section
...
wait (mutex);
In this situation, several processes may be executing in their critical sections
simultaneously, violating the mutual-exclusion requirement. This error may be discovered
only if several processes are simultaneously active in their critical sections.
 Suppose that a process replaces signal (mutex) with wait (mutex). That is, it executes
wait (mutex);
...
critical section
...
wait (mutex);
In this case, a deadlock will occur.
 Suppose that a process omits the wait (mutex), or the signal (mutex), or both. In this
case, either mutual exclusion is violated or a deadlock will occur.

19
To deal with such errors, researchers have developed one fundamental high-level
synchronization construct—the monitor type.

1. Monitor Usage
A monitor type is an ADT that includes a set of programmer defined operations that
are provided with mutual exclusion within the monitor. The monitor type also declares the
variables whose values define the state of an instance of that type, along with the bodies of
functions that operate on those variables.
Monitor monitorname
{ /* shared variable declarations */
function P1 ( . . . ) { . . .
}
function P2 ( . . . ) { . . .
}
.
.
.
function Pn ( . . . ) { . . .
}
initialization code ( . . . ) { . . .
}
}
The monitor construct ensures that only one process at a time is active within the
monitor. Consequently, the programmer does not need to code this synchronization constraint
explicitly.

Condition Variables: The monitor construct is not sufficiently powerful for modelling some
synchronization schemes. For this purpose, we need to define additional synchronization
mechanisms. These mechanisms are provided by the condition construct,
condition x, y;
The only operations that can be invoked on a condition variable are wait () and signal
(). The operation
x.wait ();
means that the process invoking this operation is suspended until another process invokes
x.signal ();
20
The x.signal () operation resumes exactly one suspended process. If no process is suspended,
then the signal () operation has no effect.

2. Dining-Philosophers Solution Using Monitors


Monitor concepts presents a deadlock-free solution to the dining-philosophers
problem. This solution imposes the restriction that a philosopher may pick up her chopsticks
only if both of them are available.
The following are the data structure:
enum {THINKING, HUNGRY, EATING} state [5];
Philosopher i can set the variable state[i] = EATING only if her two neighbours are
not eating: (state[(i+4) % 5] != EATING) and(state[(i+1) % 5] != EATING).
We also need to declare: condition self [5];
This allows philosopher i to delay herself when she is hungry but is unable to obtain
the chopsticks she needs.
monitor DiningPhilosophers
{
enum {THINKING, HUNGRY, EATING} state [5];
condition self [5];
void pickup (int i)
{
state[i] = HUNGRY;
test (i);
if (state[i]! = EATING)
self[i].wait ();
}
void putdown (int i)
{
state[i] = THINKING;
test ((i + 4) % 5);
test ((i + 1) % 5);
}
void test (int i)
{
if ((state [(i + 4) % 5]! = EATING) && (state[i] == HUNGRY) && (state [(i
+ 1) % 5]! = EATING))
{
state[i] = EATING;
self[i].signal ();
}
}
initialization code ()
{
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}

21
}

3. Implementing a Monitor Using Semaphores


Variables
The following are the variables,
semaphore mutex=1;
For each monitor, a semaphore mutex (initialized to 1) is provided. A process must
execute wait (mutex) before entering the monitor and must execute signal (mutex) after
leaving the monitor.
semaphore next=0;
Since a signalling process must wait until the resumed process either leaves or waits,
an additional semaphore, next, is introduced, The signalling processes can use next to
suspend themselves
int next_count = 0;
An integer variable next count is also provided to count the number of processes
suspended on next.

Procedure
Each procedure F will be replaced by,
wait (mutex);
...
body of F
...
if (next _count > 0)
signal (next);
else
signal (mutex);
Mutual exclusion within a monitor is ensured.

Condition Variables
For each condition x, we introduce a semaphore x _sem and an integer variable x _count,
both initialized to 0.
The operation x.wait () can now be implemented as,
x_count++;
if (next_count > 0)
signal (next);
else
signal (mutex);
wait (x_sem);
x_count--;

The operation x.signal () can be implemented as,

if (x_count > 0)
{

22
next_count++;
signal (x_sem);
wait (next);
next_count--;
}

4. Resuming Processes within a Monitor


If several processes are suspended on condition x, and an x.signal () operation is
executed by some process, then we have to determine which of the suspended processes
should be resumed next.
First-Come, First-Served (FCFS)
One simple solution is to use a first-come, first-served (FCFS) ordering, so that the
process that has been waiting the longest is resumed first. This is a simple scheduling scheme
but not adequate.
Conditional-Wait
This construct has the form
x.wait(c);
where c is an integer expression that is evaluated when the wait () operation is
executed. The value of c, which is called a priority number, is then stored with the name of
the process that is
suspended. When x.signal () is executed, the process with the smallest priority number is
resumed next.
ResourceAllocator monitor
It controls the allocation of a single resource among competing processes. Each
process, when requesting an allocation of this resource, specifies the maximum time it plans
to use the resource. The monitor allocates the resource to the process that has the shortest
time-allocation request. A process that needs to access the resource in question must observe
the following sequence:
R.acquire(t);
...
access the resource;
...
R.release();
where R is an instance of type ResourceAllocator.
Monitor to allocate a single resource
monitor ResourceAllocator
{
boolean busy;
condition x;
void acquire(int time)
{
if (busy)
x.wait(time);
busy = true;
}

23
void release()
{
busy = false;
x.signal();
}
initialization code()
{
busy = false;
}
}
Problems
The following problems can occur:
 A process might access a resource without first gaining access permission to the
resource.
 A process might never release a resource once it has been granted access to the
resource.
 A process might attempt to release a resource that it never requested.
 A process might request the same resource twice (without first releasing the resource).
One possible solution is to include the resource access operations within the
ResourceAllocator monitor.

24
UNIT-3
INTERPROCESS COMMUNICATION MECHANISMS
Cooperating processes
Processes executing concurrently in the operating system may be either independent
processes or cooperating processes.
Independent Process: 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 data with any other
process is independent.
Cooperating Process: 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.

Reasons for providing cooperation


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 such information.
 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. Notice that such a
speedup can be achieved only if the computer has multiple processing cores.
 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.

IPC between processes on a single computer system, IPC between processes


on different systems
Cooperating processes require an inter-process communication (IPC) mechanism
that will allow them to exchange data and information.
The following are the different forms of IPC mechanisms,
 Pipes
 FIFOs
 Message Queues
 Shared memory
 Sockets
 Steams
The first 4 are usually restricted to IPC between processes on the same host. The final
two are the only support IPC between processes on different hosts.

Pipes
Definition: A pipe acts as a conduit or channel allowing two processes to communicate.
Pipes are the oldest form of IPC
Common types of pipes
1
Two common types of pipes used on both UNIX and Windows systems:
 Ordinary pipes or Pipes
 Named pipes or FIFOs

Ordinary pipes or Pipes


 Ordinary pipes allow two processes to communicate in standard producer– consumer
fashion: the producer writes to one end of the pipe (the write-end) and the consumer reads
from the other end (the read-end).
 Ordinary pipes are unidirectional, allowing only one-way communication. If two-way
communication is required, two pipes must be used, with each pipe sending data in a
different direction.
 On UNIX systems, A pipe is created by calling the pipe function
General Form: int pipe(int fd[2]);
Two file descriptors are returned through the fd argument: fd[0] is open for reading,
and fd[1] is open for writing. The output of fd[1] is the input for fd[0].
 Two ways to picture a half-duplex pipe

 A pipe in a single process is next to useless.


 The process that calls pipe then calls fork, creating an IPC channel from the parent to the
child, or vice versa.

 What happens after the fork depends on which direction of data flow we want.
a) For a pipe from the parent to the child, the parent closes the read end of the pipe (fd[0]),
and the child closes the write end (fd[1]).

2
b) For a pipe from the child to the parent, the parent closes fd[1], and the child closes
fd[0].

 When one end of a pipe is closed, two rules apply.


a) If we read from a pipe whose write end has been closed, read returns 0 to indicate an
end of file after all the data has been read.
b) If we write to a pipe whose read end has been closed, the signal SIGPIPE is generated
which is either ignored or catched. PIPE_BUF specifies the kernel’s pipe buffer size.
 Ordinary pipes on Windows systems are termed anonymous pipes. They employ parent–
child relationships between the communicating processes. In addition, reading and writing
to the pipe can be accomplished with the ordinary ReadFile () and WriteFile () functions.
The Windows API for creating pipes is the CreatePipe() function.
 Ordinary pipes provide a simple mechanism for allowing a pair of processes to
communicate. However, ordinary pipes exist only while the processes are communicating
with one another. On both UNIX and Windows systems, once the processes have finished
communicating and have terminated, the ordinary pipe ceases to exist.

Named Pipes or FIFOs


 Named pipes are referred to as FIFOs in UNIX systems.
 Both UNIX and Windows systems support named pipes.
 Named pipes provide a much more powerful communication tool.
 Communication can be bidirectional
 Unnamed pipes can be used only between related processes when a common ancestor
has created the pipe. With FIFOs, however, unrelated processes can exchange data.
 Creating a FIFO is similar to creating a file.
General Form: int mkfifo(const char *path, mode_t mode);
 Once we have used mkfifo to create a FIFO, we open it using open. Normal file I/O
functions(close, read,write,unlink etc) all work with FIFOs.
 There are two uses for FIFOs.

3
a) Duplication of Output stream
b) Client server communication
 Using FIFOs to Duplicate Output Streams
FIFOs can be used to duplicate an output stream and can be used for nonlinear
connections.

With a FIFO and the UNIX program tee(1), we can accomplish this procedure without
using a temporary file. (The tee program copies its standard input to both its standard output
and the file named on its command line.)
mkfifo fifo1
prog3 < fifo1 &
prog1 < infile | tee fifo1 | prog2
We create the FIFO and then start prog3 in the background, reading from the FIFO. We
then start prog1 and use tee to send its input to both the FIFO and prog2.

 Client–Server Communication Using a FIFO


Another use for FIFOs is to send data between a client and a server. If we have a
server that is contacted by numerous clients, each client can write its request to a well-known
FIFO that the server creates.

The problem in using FIFOs for this type of client–server communication is how to
send replies back from the server to each client. A single FIFO can’t be used, as the clients
would never know when to read their response versus responses for other clients. One

4
solution is for each client to send its process ID with the request. The server then creates a
unique FIFO for each client, using a pathname based on the client’s process ID.

 Although FIFOs allow bidirectional communication, only half-duplex transmission is


permitted. If data must travel in both directions, two FIFOs are typically used.
Additionally, the communicating processes must reside on the same machine. If inter
machine communication is required, sockets must be used.
 Named pipes on Windows systems provide a richer communication mechanism than
their UNIX counterparts.
 Full-duplex communication is allowed, and the communicating processes may reside on
either the same or different machines.
 Named pipes are created with the CreateNamedPipe () function and a client can connect
to a named pipe using ConnectNamedPipe ().
 Communication over the named pipe can be accomplished using the ReadFile () and
WriteFile () functions.

Message Queues
 A message queue is a linked list of messages stored within the kernel and identified by a
message queue identifier.
 Each queue has the following msqid_ds structure associated with it:
struct msqid_ds
{
struct ipc_perm msg_perm; /* defines permissions */
msgqnum_t msg_qnum; /* # of messages on queue */
msglen_t msg_qbytes; /* max # of bytes on queue */
pid_t msg_lspid; /* pid of last msgsnd() */
pid_t msg_lrpid; /* pid of last msgrcv() */
time_t msg_stime; /* last-msgsnd() time */
time_t msg_rtime; /* last-msgrcv() time */
time_t msg_ctime; /* last-change time */
.
.
.
5
};
This structure defines the current status of the queue.
 msgget
The first function normally called is msgget to either open an existing queue or create
a new queue.
General Form: int msgget(key_t key, int flag);
Key is converted into identifier of the process and used to decide whether a new
queue is created or an existing queue is referenced.
When a new queue is created, the following members of the msqid_ds structure are
initialized.
 The mode member of this structure is set to the corresponding permission bits of flag.
 msg_qnum, msg_lspid, msg_lrpid, msg_stime, and msg_rtime are all set to 0.
 msg_ctime is set to the current time.
 msg_qbytes is set to the system limit.

On success, msgget returns the non-negative queue ID. This value is then used with
the other three message queue functions.
 msgsnd
Data is placed onto a message queue by calling msgsnd.
General Form: int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
Each message is composed of a positive long integer type field, a non-negative length
(nbytes), and the actual data bytes (corresponding to the length). Messages are always placed
at the end of the queue.
The ptr argument points to a long integer that contains the positive integer message
type, and it is immediately followed by the message data.

Message structure:
struct mymesg
{
long mtype; /* positive message type */
char mtext[512]; /* message data, of length nbytes */
};
The ptr argument is then a pointer to a mymesg structure. The message type can be
used by the receiver to fetch messages in an order other than first in, first out.

A flag value of IPC_NOWAIT can be specified, this causes msgsnd to return an error
message when the queue is full.

When msgsnd returns successfully, the msqid_ds structure associated with the
message queue is updated to indicate the process ID that made the call (msg_lspid), the time
that the call was made (msg_stime), and that one more message is on the queue (msg_qnum).
 msgrcv
Messages are retrieved from a queue by msgrcv.
General Form: ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);

6
the ptr argument points to a long integer (where the message type of the returned
message is stored) followed by a data buffer for the actual message data. nbytes specifies the
size of the data buffer.
If the returned message is larger than nbytes and the MSG_NOERROR bit in flag is
set, the message is truncated.
The type argument lets us specify which message we want.
type == 0 The first message on the queue is returned.
type >0 The first message on the queue whose message type equals type is
returned.
type <0 The first message on the queue whose message type is the lowest value
less than or equal to the absolute value of type is returned.
type= nonzero Used to read the messages in an order other than first in, first out such
as using priority

We can specify a flag value of IPC_NOWAIT to make the operation nonblocking, If


IPC_NOWAIT is not specified, the operation blocks until a message of the specified type is
available, the queue is removed from the system (−1 is returned with errno set to EIDRM), or
a signal is caught and the signal handler returns

When msgrcv succeeds, the kernel updates the msqid_ds structure associated with the
message queue to indicate the caller’s process ID (msg_lrpid), the time of the call
(msg_rtime), and that one less message is on the queue (msg_qnum).
 msgctl
The msgctl function performs various operations on a queue.
General Form: int msgctl(int msqid, int cmd, struct msqid_ds *buf );
The cmd argument specifies the command to be performed on the queue specified by msqid.
IPC_STAT Fetch the msqid_ds structure for this queue, storing it in the structure
pointed to by buf.
IPC_SET Copy the following fields from the structure pointed to by buf to the
msqid_ds structure associated with this queue: msg_perm.uid,
msg_perm.gid, msg_perm.mode, and msg_qbytes.
IPC_RMID Remove the message queue from the system and any data still on the
queue. This removal is immediate.

Shared memory
 Shared memory allows two or more processes to share a given region of memory.
 This is the fastest form of IPC, because the data does not need to be copied between the
client and the server.
 The only trick in using shared memory is synchronizing access to a given region among
multiple processes. If the server is placing data into a shared memory region, the client
shouldn’t try to access the data until the server is done. Often, semaphores are used to
synchronize shared memory access.

7
 The kernel maintains a structure with at least the following members for each shared
memory segment:
struct shmid_ds
{
struct ipc_perm shm_perm; /* defines permissions */
size_t shm_segsz; /* size of segment in bytes */
pid_t shm_lpid; /* pid of last shmop() */
pid_t shm_cpid; /* pid of creator */
shmatt_t shm_nattch; /* number of current attaches */
time_t shm_atime; /* last-attach time */
time_t shm_dtime; /* last-detach time */
time_t shm_ctime; /* last-change time */
.
.
.
};
 shmget
The first function called is usually shmget, to obtain a shared memory identifier
General Form: int shmget(key_t key, size_t size, int flag);
Key is converted into an identifier and whether a new segment is created or an
existing segment is referenced.

When a new segment is created, the following members of the shmid_ds structure are
initialized.
 The ipc_perm structure is initialized . The mode member of this structure is set to the
corresponding permission bits of flag.
 shm_lpid, shm_nattch, shm_atime, and shm_dtime are all set to 0.
 shm_ctime is set to the current time.
 shm_segsz is set to the size requested.
The size parameter is the size of the shared memory segment in bytes.
 shmctl
The shmctl function is the catchall for various shared memory operations.
General Form: int shmctl(int shmid, int cmd, struct shmid_ds *buf );
The cmd argument specifies one of the following five commands to be performed, on
the segment specified by shmid.
IPC_STAT Fetch the shmid_ds structure for this segment, storing it in the structure
pointed to by buf.

8
IPC_SET Set the following three fields from the structure pointed to by buf in
the shmid_ds structure associated with this shared memory segment:
shm_perm.uid, shm_perm.gid, and shm_perm.mode.
IPC_RMID Remove the shared memory segment set from the system.

Two additional commands are provided by Linux and Solaris, but are not part of the
Single UNIX Specification.
SHM_LOCK Lock the shared memory segment in memory. This command can be
executed only by the superuser.
SHM_UNLOCK Unlock the shared memory segment. This command can be executed
only by the superuser
 shmat
Once a shared memory segment has been created, a process attaches it to its address
space by calling shmat.
General Form: void *shmat(int shmid, const void *addr, int flag);
The address in the calling process at which the segment is attached depends on the
addr argument and whether the SHM_RND bit is specified in flag.
 If addr is 0, the segment is attached at the first available address selected by the
kernel. This is the recommended technique.
 If addr is nonzero and SHM_RND is not specified, the segment is attached at the
address given by addr.
 If addr is nonzero and SHM_RND is specified, the segment is attached at the address
given by (addr − (addr modulus SHMLBA)). The SHM_RND command stands for
‘‘round.’’ SHMLBA stands for ‘‘low boundary address multiple’’ and is always a
power of 2.

The value returned by shmat is the address at which the segment is attached, or −1 if
an error occurred.
If shmat succeeds, the kernel will increment the shm_nattch counter in the shmid_ds
structure associated with the shared memory segment.
 shmdt
When we’re done with a shared memory segment, we call shmdt to detach it. This
does not remove the identifier and its associated data structure from the system. The identifier
remains in existence until some process (often a server) specifically removes it by calling
shmctl with a command of IPC_RMID.
General Form: int shmdt(const void *addr);
The addr argument is the value that was returned by a previous call to shmat. If
successful, shmdt will decrement the shm_nattch counter in the associated shmid_ds
structure.

You might also like