Unit 2-1
Unit 2-1
zombie process
A process that has terminated, but whose parent has not yet called
wait(), is known as a zombie process.
Inter process Communication
• Processes executing concurrently in the operating system may be either
independent processes or cooperating processes
Independent 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 data with any other process is
independent
Cooperating
• A process is cooperating if it can affect or be affected by the other
processes executing in the system.
Reasons for Interprocess Communication
1. Information sharing.
2. Computation speedup.
3. Modularity.
4. Convenience.
Reasons for Interprocess Communication
• 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.
Processes communication
• Processes can communicate with each other through
both:
1. Shared Memory
2. Message passing
Shared Memory- 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.
Message passing- In the message-passing model, communication takes
place by means of messages exchanged between the cooperating
processes.
• send (message)
• received (message)
Shared-Memory Systems
• 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.
• The processes are also responsible for ensuring that they are not
writing to the same location simultaneously.
Example producer–consumer problem,
• A producer process produces information that is consumed by a
consumer process.
• 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.
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.
Buffer
• Two types of buffers can be used
1. unbounded buffer
2. bounded buffer
The unbounded buffer places no practical limit on the size of the buffer. The
producer can always produce new items.
The bounded buffer assumes a fixed buffer size. the producer must wait if the
buffer is full
• Count will be incremented when product is placed by producer in
buffer.
• Count will be decremented when product is consumed by consumer
from buffer.
Producer side
#define BUFFER SIZE 10 • In points to the next free position in
the buffer.
typedef struct • Out points to the first full position in
{ the buffer.
... • In==Out is full
}item;
item buffer[BUFFER SIZE];
int in = 0;
int out = 0;
Consumer side
item next consumed;
while (true)
{
while (in == out)
; /* do nothing */
next consumed = buffer[out];
out = (out + 1) % BUFFER SIZE;
/* consume the item in next consumed */
}
Message passing
In the message-passing model, communication takes place by means of
messages exchanged between the cooperating processes.
• send (message)
• received (message)
• 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.
Several methods for logically implementing a link and the
send()/receive() operations:
1. •Direct or indirect communication(Naming)
2. •Synchronous or asynchronous communication(Synchronization)
3. •Automatic or explicit buffering
https://notesformsc.org/message-passing-system/
Direct Communication
In direct communication, processes must explicitly name the sender or
receiver in the communication.
The send() and receive() are defined as:
• send(P, message) - send a message to process P
• receive(Q, message) - receive a message from process Q
Indirect Communication
• In indirect communication, messages are sent and received from
mailboxes or ports. The processes can place messages into a mailbox
or remove messages from them. The mailbox has a unique
identification.
• send(M, message) - send a message to mailbox M
• receive(M, message) - recieve a message from mailbox M
Synchronous or asynchronous communication
• Message passing may be either blocking or nonblocking— also known
as synchronous and asynchronous.
1. Blocking send. The sending process is blocked until the message is
received by the receiving process or by the mailbox.
2. Nonblocking send. The sending process sends the message and
resumes operation.
3. Blocking receive. The receiver blocks until a message is available.
4. Nonblocking receive. The receiver retrieves either a valid message
or a null.
Automatic and Explicit Buffering
The messages exchanged between communicating processes resides in a
temporary queue. The queue can be implemented in three way:
1.Zero capacity – with zero capacity the link cannot have messages
waiting. Blocking send is the correct option.
2.Bounded capacity – the queue is of finite length n. If queue not full,
sender can continue to send messages. If full, blocking send.
3.Unbounded capacity – Infinite queue length; any number of
messages wait and sender never blocks.
Communication in Client –Server Systems
1. Sockets,
2. Remote procedure calls (RPCs),
3. Pipes
Sockets
• Sockets facilitate communication between two processes on
the same machine or different machines.
• They are used in a client/server framework and consist of the
IP address and port number.
• If a client on host X with IP address 146.86.5.20 with port
1625 wishes to establish a connection with a web server
(which is listening on port 80) at address 161.25.19.8. The
connection will consist of a pair of sockets:
(146.86.5.20:1625) on host X and (161.25.19.8:80) on the
web server.
Remote Procedure Calls
• Remote Procedure Call is a software communication
protocol that one program can use to request a service
from a program located in another computer on
a network without having to understand the network's
details.
Stub
• Acts as the representative of the remote procedure code.
• A separate stub exists for each separate remote procedure.
• When the client invokes a remote procedure, the RPC system
calls the appropriate stub, passing it the parameters provided
to the remote procedure.
• This stub locates the port on the server and marshals the
parameters. Parameter marshalling involves packaging the
parameters into a form that can be transmitted over a
network.
• The stub then transmits a message to the server using
message passing. A similar stub on the server side receives
this message and invokes the procedure on the server.
• First a client will sends RPC message to matchmaker
requesting the port address of the RPC it needs to execute.
• Matchmaker will find the port number and replies to the
client with port number.
• The port number is returned, and the RPC calls can be sent
to that port until the process terminates (or the server
crashes).
Pipes
• A pipe simply refers to a temporary software connection between two
processes.
• A pipe works on the first in, first out principle and behaves like a
queue data structure.
• With a pipe, the output of the first process becomes the input of the
second. However, the reverse does not happen. This is why a pipe is a
form of one-way communication between processes.
Two Types:
1. ordinary pipes
2. named pipe
1.Ordinary 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).
• As a result, 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, ordinary pipes are constructed using the function
pipe(int fd[])
• fd[0] is the read-end of the pipe, and fd[1] is the write-end
• An ordinary pipe cannot be accessed from outside the process that
created it. Typically, a parent process creates a pipe and uses it to
communicate with a child process that it creates via fork().
• The parent process creates a pipe and then sends a fork() call
creating the child process.
create the pipe Parent process and Writing(if (pid > 0))
if (pipe(fd) == -1) /* close the unused end of the pipe */
close(fd[READ END]);
{
/* write to the pipe */
fprintf(stderr,"Pipe failed");
write(fd[WRITE END], write msg, strlen(write msg));
return 1; /* close the write end of the pipe */
} close(fd[WRITE END]);
}
fork a child process Child process and Reading (else )
pid = fork(); /* close the unused end of the pipe */
close(fd[WRITE END]);
if (pid < 0) { /* error occurred */
/* read from the pipe */
fprintf(stderr, "Fork Failed");
read(fd[READ END], read msg, BUFFER SIZE);
return 1;
printf("read %s",read msg);
} /* close the Read end of the pipe */
close(fd[READ END]); }
2.Named Pipes
• 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. once the processes have finished communicating and
have terminated, the ordinary pipe ceases to exist.
• Named pipes provide a much more powerful communication tool.
Communication can be bidirectional, and no parent–child relationship is
required. Once a named pipe is established, several processes can use it for
communication.
• Named pipes continue to exist after communicating processes have finished
• Named pipes, also known as FIFOs (First In, First Out), are a form of inter-
process communication.
Creating a Named Pipe:
• On UNIX systems, Names pipes are constructed using the function
mkfifo(my_pipe)
• Example
int main()
{
mkfifo("my_pipe", 0666);
return 0;
}
Writing :
int fd = open("my_pipe");
write(fd, "Hello, Pipe!", 13);
close(fd);
Reading:
int fd = open("my_pipe");
char buffer[256];
read(fd, buffer, sizeof(buffer));
close(fd);
Process Synchronization
• Process Synchronization is the task of coordinating the execution
of processes in a way that no two processes can have access to the
same shared data and resources.
• It is specially needed in a multi-process system when multiple
processes are running together, and more than one processes try to
gain access to the same shared resource or data at the same time.
• The main objective of process synchronization is to ensure that
multiple processes access shared resources without interfering with
each other and to prevent the possibility of inconsistent data due to
concurrent access.
• To achieve this, various synchronization techniques such as
semaphores, monitors, and critical sections are used.
Critical-Section Problem
int x = 10;
// Process 1 // Process 2
int s = 10; int s = 10;
int u = 20; int u = 20;
x = s + u; x = s - u;
• The task of the Producer is to produce the item, put it into the
memory buffer, and again start producing items. Whereas the task of
the Consumer is to consume the item from the memory buffer.
• The producer should produce data only when the buffer is not full. In
case it is found that the buffer is full, the producer is not allowed to
store any data into the memory buffer.
• Data can only be consumed by the consumer if and only if the memory
buffer is not empty. In case it is found that the buffer is empty, the
consumer is not allowed to use any data from the memory buffer.
• Accessing memory buffer should not be allowed to producer and
consumer at the same time.
• Count will be incremented when product is placed by producer in
buffer.
• Count will be decremented when product is consumed by consumer
from buffer.
Accessing memory buffer should not be allowed to producer and
consumer at the same time.
• To solve this problem, we need two counting semaphores – Full and
Empty.
• “Full” keeps track of number of items in the buffer at any given time
• “Empty” keeps track of number of unoccupied slots.
• When producer produces an item
then the value of “empty” is
Solution for Producer reduced by 1 because one slot will
Do be filled now.
• The value of mutex is also reduced
{ to prevent consumer to access the
//produce an item buffer.
wait(empty); • Now, the producer has placed the
wait(mutex); item and thus the value of “full” is
//place in buffer increased by 1.
signal(mutex); • The value of mutex is also
signal(full); increased by 1 because the task of
producer has been completed and
}while(true) consumer can access the buffer.
• As the consumer is removing an
item from buffer, therefore the
Solution for Consumer
value of “full” is reduced by 1
Do • the value is mutex is also
{ reduced so that the producer
cannot access the buffer at this
wait(full); moment.
wait(mutex); • Now, the consumer has
// consume item from buffer consumed the item, thus
increasing the value of “empty”
signal(mutex); by 1.
signal(empty); • The value of mutex is also
}while(true) increased so that producer can
access the buffer now
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.
• 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, confusion may occur.
• 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.
Conditions for reader and writers problem
• No reader be kept waiting unless a writer has already obtained
permission to use the shared object.
• Once a writer is ready, that writer perform its write as soon as
possible.
Solution to the first readers–writers problem
The reader processes share the following 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
process.
• The read count variable keeps track of how many processes are
currently reading the object
structure of a writer process
do
{
wait(rw_mutex); ...
/* writing is performed */ ...
signal(rw_mutex);
} while (true);
Structure of a reader process
do {
wait(mutex);
read count++;
if (read count == 1)
wait(rw_mutex);
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
read count--;
if (read count == 0)
signal(rw_mutex);
signal(mutex);
• 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.
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 center 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 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 the chopsticks. When she is finished eating, she puts
down both chopsticks and starts thinking again.
• It is a simple representation of the need to allocate several resources
among several processes in a deadlock-free and starvation-free
manner.
• 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.
semaphore chopstick[5];
do {
wait(chopstick[i]); \\left
wait(chopstick[(i+1) % 5]); \\right
...
/* eat for awhile */
...
signal(chopstick[i]);
signal(chopstick[(i+1) % 5]);
...
/* think for awhile */
...
} while (true);
Wait Operation:
• wait(chopstick[i]): Philosopher i attempts to acquire the semaphore
for the chopstick on their left.
• wait(chopstick[(i+1) % 5]): Philosopher i attempts to acquire the
semaphore for the chopstick on their right. The modulo operation (%
5) is used to ensure that the index stays within the valid range (0 to 4).
Several possible remedies to the deadlock problem are replaced by:
1. Allow at most four philosophers to be sitting simultaneously at the
table.
2. 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).
3. 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 timing errors that are difficult to
detect, since these errors happen only if particular execution
sequences take place and these sequences do not always
occur.
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
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
• To deal with such errors, researchers have developed high-level
language constructs. – monitor
• An abstract data type—or ADT—encapsulates data with a set of
functions to operate on that data that are independent of any specific
implementation of the ADT.
• A monitor type is an ADT that includes a set of programmer defined
operations that are provided with mutual exclusion within the
monitor.
• A monitor type also contains declaration of variables, along with the
functions that operates on those variable.
• A function defined within a monitor can access only those variables
declared locally within the monitor and its formal parameters.
Similarly, the local variables of a monitor can be accessed by only the
local functions.
• The monitor construct ensures that only one process at a time is
active within the monitor.
monitor monitor name
{
/* shared variable declarations */ function Pn(...) {
function P1(...) { ...
... }
} initialization code (...) {
function P2(...) { ...
... }
} }
Condition Construct:
condition x, y(condition variable)
• The only operations that can be invoked on a condition variable are wait()
and signal()