Chapter2 - Processes and Threads
Chapter2 - Processes and Threads
Process
• Processes are one of the oldest and most important abstractions that
operating systems provide.
• They support the ability to have (pseudo) concurrent operation even
when there is only one CPU available. They turn a single CPU into
multiple virtual CPUs. Without the process abstraction, modern
computing could not exist
Process management
Process : is an instant of program in execution
The entity that can be assigned to and executed on a processor
Keeping track of multiple parallel activities is hard, So OS designer have evolved a conceptual
model where all the runnable software on the computer, sometimes including OS is
organized into a number of sequential processes
3. Error exit : error cause by process Examples include executing an illegal instruction, referencing
nonexistent memory, or dividing by zero.
4. Killed by another process Unix : kill system call .Win32 function is TerminateProcess
Process Hierarchies
IN UNIX there is concepts of process group. In UNIX, a process and all of its children and further
descendants together form a process group. Tree structure
e.g. init at the root
Windows does not have any concept of process hierarchy. The only hint of a process hierarchy is that
when a process is created, the parent is given a special token (called a handle) that it can use to control
the child. However, it is free to pass this token to some other process, thus invalidating the hierarchy.
Processes in UNIX cannot disinherit their children.
Process States
Figure 3.2
In the following process state transition diagram for a uniprocessor system,
assume that there are always some processes in the ready state: Now
consider the following statements:
(A) I and II
(B) I and III
(C) II and III
(D) II and IV
Implementation of process
To implement the process model os maintains a table( an array of structures)called Process table, with one entry
per process (PCB or TCB)
This entry contains following information
State, its Pc, stack pointer, memory allocation, the status of its open files, its accounting and scheduling
information and everything else about the process that must be saved when the process is switched from running
to ready or blocked state, so that it can be restarted later
Suppose that user process 3 is running when a disk interrupt happens.
A process may be interrupted thousands of times during its execution, but the
key idea is that after each interrupt the interrupted process returns to precisely the
same state it was in before the interrupt occurred.
Modeling Multiprogramming
When multiprogramming is used, the CPU utilization can be improved.
Crudely put, if the average process computes only 20% of the time it is sitting in
memory, then with five processes in memory at once the CPU should be busy all
the time.
Adding yet another 8 GB would increase CPU utilization only from 79% to 91%, thus raising
the throughput by only another 12%.
Using this model, the computer’s owner might decide that the first addition was a good
investment but that the second was not.
Threads
Process Thread
Each process
resource has anand
grouping address space and a single thread
execution of control.
Execution So itincan
control do one
process task at a time.
or entities
Now in many
A process hasOs it is possible
address to have of
space consists multiple
text, threads of control
scheduled for in the same
execution onaddress
the CPUspace running in quasi parallel so
process
data andcan performlike
resources more
openthan one
files, task at a time Thread has Pc, registers which hold current
child
processes, pending alarm, signal handlers, working variables, and has a stack which
accounting information and more contains execution history
IPC required They have some of the properties of processes,
they are called lightweight processes
Thread share address space (code and data) so
they share global variables
No inter thread communication mechanism
required, no protection required (a process is
always owned by a single user, who has likely created
multiple threads so that they can cooperate, not fight.)
The main reason for having threads is that in many applications, multiple activities are going on at once. Some of these
may block from time to time. By decomposing such an application into multiple sequential threads that run in quasi-
parallel, the programming model becomes simpler
Each thread will generally call different procedures and thus have a different
execution history. This is why each thread needs its own stack
Thread concept has the ability for multiple threads of execution to share a set of resources so they can work together
closely to perform some task
Thread state and state transition diagram is same as process
Process start with single thread. It has the ability to create new threads by calling a library procedure e.g.
thread_create and returns thread id
Thread_exit, thread_wait, thread_yield
In general thread creation and termination is very much like process creation and termination
Advantages of Multithreading
1. Responsiveness :Main reason for having threads is that in may applications multiple activates are going on
at once some of these may block from time to time. By decomposing such an application into multiple
sequential threads that run in quasi-parallel, the programming model becomes simpler and increasing
responsiveness
E.g. Multithreaded web browser could still allow user interaction in one thread while an image is being loaded
into another thread
2. Resource sharing :Sharing of address space and all of its data. Only now with threads we add a new element:
the ability for the parallel entities to share an address space and all of its data among themselves.
3.Economy:They do not have any resource attached to them they are easier to create and destroy than
process.in many systems creation of thread is 100times faster than creating a process. When number of
threads needed changes dynamically and rapidly this property is useful
4.Performace improvement :Threads yield no performance gain when all of them are CPU bound but when
there is sufficient computing and I/o, threads allows these activities to overlap thus speeding up the
application.
5.Utilization of multiprocessor architecture: Threads are useful on system with multiple CPUS, where real
parallelism is possible.
e.g. word processors
One thread interacts with user , one thread do formatting and third thread save entire file to disk every few minutes
Multiple process it will not work because here there is need of sharing document
If it is single thread then whenever a disk backup started commands from keyboard and mouse would be ignored until the
backup was finished
e.g. web server
Web server can be organized as
One thread called dispatcher read incoming requests for work
from the network
After examining the request it chooses an idle(i.e., blocked)
worker thread and hands it the request
When the worker thread run it check requested page
available in cache otherwise start read operation and block
until the disk operation completed
When the thread blocks on the disk operation, another
thread is chosen to run, possibly the dispatcher, in order to
acquire more work, or possibly another worker that
is now ready to run.
Consider webserver as single thread. The main loop of web
server gets a request, examines it and carries it out to
completion before getting the next one. While waiting for the
disk, the server is idle. So it can handle only few requests/sec
(throughput)
In this problem you are to compare reading a file using a single threaded file server and a multithreaded server.
It takes 15 msec to get a request for work, dispatch it and do the rest of the necessary processing assuming that
the data needed are in the block cache. If a disk operation is needed as in the case one third of the time an
additional 90msec is required during which time the thread sleeps. How many requests/sec can the server handle
if it is single threaded? If it is multithreaded?
In the single-threaded case, the cache hits take 15 msec and cache misses take
90 msec. The weighted average is 2/3 × 15 + 1/3 × 90. Thus the mean request
takes 40 msec and the server can do 25 per second.
For a multithreaded server, all the waiting for the disk is overlapped, so every request
takes 15msec, and the server can handle 66 .66 requests per second.
Implementation of threads
Two ways
1. User level
2. Kernel level
User level
User level threads: supported above the kernel and are implemented by a thread library at the user level. Kernel knows
nothing about them. e.g. POSIX threads, Java threads. Threads run on top of runtime system which is a collection of
procedures that manage threads
thread table to keep track of thread, which is managed by run time system
Advantages
1. Multithreading application run on top of os do not support threads by implementing thread package on top of an Os in
user level
2. Thread switching, creation is faster because no mode change take place
3. Thread scheduling is faster
4. Each process to have its own customized scheduling algorithm and they also scale better
Disadvantages
1. one thread execute system call, may involved blocking then entire process blocked. solution :1. non blocking system call.
2.before invoking system call it check is it safe to invoke system call? The code placed around the system call to do the
checking is called a jacket or wrapper. In most versions of UNIX, a system call, select, exists, which allows the caller to tell
whether a prospective read will block
Scheduler activation
When the kernel knows that a thread has blocked, the kernel notifies the process run time system, passing as
parameters on the stack the number of the thread and the description of the event. This mechanism is called an upcall
Once activated, the run-time system can reschedule its threads, typically by marking the current thread as blocked and
taking another thread from the ready list
Later, when the kernel learns that the original thread can run again (e.g., the pipe it was trying to read from now
contains data, or the page it faulted over has been brought in from disk), it makes another upcall to the run-time system
to inform it. The run-time system can either restart the blocked thread immediately or put it on the ready list to be run
later.
An objection to scheduler activations is the fundamental dependence on upcalls, a concept that violates the structure
inherent in any layered system. Normally, layer n offers certain services that layer n + 1 can call on, but layer n may not
call procedures in layer n + 1. Upcalls do not follow this fundamental principle.
2 one thread cause page fault, entire process block
3.if thread do not releases control of thread other thread will not get control
Solution : run time system request kernel to give notification at every second…
Kernel level
Kernel support threads and manage threads. No run time system is needed
Windows NT, Windows 2000, solaris 2,..
There is no thread table in each process. Instead the kernel has a thread table that keeps track of all the threads in
system. When a thread wants to create a new thread or destroy an existing thread, it makes a kernel call, which then
does the creation or destruction by updating the kernel thread table
Advantages : no run time system needed, when thread issue blocking system call that thread only block not whole
process , thread table is also managed by kernel
Disadvantage
Greater cost of creating and destroying threads. Some system use thread recycle i.e. when a thread is destroyed it is
marked as not runnable but its kernel data structures are kept as it is. When need arise this old thread is reactivated
While kernel threads solve some problems, they do not solve all problems. For example, what
happens when a multithreaded process forks?
Does the new process have as many threads as the old one did, or does it have just one?
In many cases, the best choice depends on what the process is planning to do next. If it is
going to call exec to start a new program, probably one thread is the correct choice,
but if it continues to execute, reproducing all the threads is probably best
Another issue is signals. Remember that signals are sent to processes, not to threads, at least in the classical model.
When a signal comes in, which thread should handle it?
Possibly threads could register their interest in certain signals, so when a signal came in it would be given to the thread
that said it wants it.
But what happens if two or more threads register for the same signal?
Hybrid
Various ways have been investigated to try to combine the advantages of user level threads with kernel level
threads
One to one mapping : drawback is creating a user thread requires creating the corresponding kernel thread
e.g. Windows NT, Windows 2000
Another way is multiplex many user level thread to a smaller or equal number of kernel thread. called many to
many e.g. Solaris 2
Kernel is aware of only kernel level threads and schedule them
Pop-UP threads
In distributed systems, how request for service is handled?
process or thread that is blocked on receive system call waiting for an incoming message. When
message arrives it accepts message and processes it
Another approach , in which arrival of message causes the system to create a new thread to
handle the message. Such a thread is called a pop-up thread. Advantage is such thread can be
created quickly because they are brand new, do not have any history. So the latency between
message arrival and start of processing can be made very short
POSIX Threads
IEEE has defined a standard for threads in IEEE standard 1003.1c
The threads package it defines is called Pthreads.
Thread call Description
Pthread_create Create a new thread
Pthread_ exit Ter minate the calling thread
Pthread_ join Wait for a specific thread to exit
Pthread_ yield Release the CPU to let another thread run
Pthread_ attr- init Create and initialize a thread’s attribute structure
Pthread_ attr_ destroy Remove a thread’s attribute structure
pthread_t pthread_self(void);
int pthread_equal(pthread_t t1, pthread_t t2); If the two threads are equal, the function returns a
non-zero value otherwise zero.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUMBER OF THREADS 10
* *
void pr int hello world(void tid)
{
pr intf("Hello World. Greetings from thread %d\n", *(int *)tid);
pthread exit(NULL);
}
*
int main(int argc, char argv[])
{
pthread t threads[NUMBER OF THREADS];
int status, i;
for(i=0; i < NUMBER OF THREADS; i++) {
pr intf("Main here. Creating thread %d\n", i);
status = pthread create(&threads[i], NULL, print hello world, (void *)&i);
if (status != 0) {
pr intf("Oops. pthread create returned error code %d\n", status);
exit(-1);
}}
for(i=0; i < NUMBER OF THREADS; i++) {
pthread_join(threads[i],NULL)}
int min;
void *findmin(void *th)
{
int *array,i,s,mint;
array = (int *)th;
mint = array[0];
for(i=0;i<SIZE/NUM_THREADS;i++)
{
if(mint>array[i])
mint = array[i];
}
if(mint < min)
min = mint;
pthread_exit(NULL);
}
for(i=0;i<NUM_THREADS;i++)
pthread_create(&thread[i],NULL,findmin,(void*)A+i*(SIZE/NUM_THREADS));
Issues involved in Making single threaded code Multithreaded
a. Global variables : errno variable
b. Reentrant procedure : second call made to any given procedure while a previous call has not yet finished
A different solution is to provide each procedure with a jacket that sets a bit to mark the library as in use. Any attempt
for another thread to use a library procedure while a previous call has not yet completed is blocked. Although this
approach can be made to work, it greatly eliminates potential parallelism.
c. Signals are delivered to process : handling signals in single threaded programs is straightforward. However it is more
complicated in multithreaded programs because process may have several threads? Where then should a signal be
delivered? Synchronous signal need to be delivered to the thread that generated the signal
Following options exist
1. Deliver the signal to the thread to which the signal applies
2. Deliver the signal to every thread in process
3. Deliver the signal to certain threads in the process
4. Assign a specific thread to receive all signals for the process , which then delivers the signal to first thread, that is
not blocking signal, used by Solaris 2.
d. Fork : if one thread calls fork does the new process duplicate all threads or is the new process single-threaded?
Some unix systems have two version of fork , one duplicate all threads and other only the thread that invoked fork
e. Stack management : when process stack overflows, kernel provides that process , required memory so stack grow
automatically. When process has multiple threads it must also have multiple stacks. When threads are implemented in
user mode kernel may not even realize that a memory fault is related to stack growth
Conclusion : introducing thread in existing system required a fairly large system redesign. The semantics of system
calls may have to be redefine and libraries have to be rewritten at the very least by maintaining backward compatibility
IPC
IPC should handle following Three issues
1. How one process can pass information to another
• Shared memory
• Message passing
2. Two or more process do not get into each others way
3. Proper sequencing when dependencies are present
Issue 2 and 3 is also applicable to thread
Shared memory #include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/ipc.h>
int main()
#include <sys/shm.h>
{
int *sum,id;
int main()
key_t key =
{ ftok("shmfile",65);
int *sum,id;
key_t key = ftok("shmfile",65); id = shmget(key, sizeof(int), IPC_CREAT |
id = shmget(key, sizeof(int), IPC_CREAT | 0666); 0666);
sum = (int *)shmat(id, 0, 0); sum = (int *)shmat(id, 0, 0);
*sum=10; printf(“%d\n”, *sum);
shmdt(sum); shmdt(sum);
} } shmctl(shmid,IPC_RMID,NULL);
Race condition
• Process that are working together may share some common storage
that each one can read and write. The shared storage may be in main
memory or it may be shared file
• Let discuss one example to understand the problem cause by this type
of IPC
Race condition
• Spooler directory
• Let there are two shared variables out and in
• Out : which points to the next file to be printed and in: which points to the next free slot in the
directory
• Situations like this, where two or more processes are reading and writing some shared data and
final results depends on who runs precisely when are called race conditions
• How do we avoid race conditions?
• Solution : Mutual exclusion, that is some way of making sure that if one process is using a shared
variable or file the other process will be excluded form doing the same thing.
• The part of the program where the shared memory is accessed is called critical region or section.
• If we could arrange matters such that no two process were ever in their critical section at the
same time we could avoid races
Mutual exclusion
• Mechanism available for mutual exclusion should satisfy following conditions
then we consider that solution as good solution
• 1. no two process may be simultaneously inside their critical regions
• 2. no assumptions may be made about speeds and number of CPUs
• 3. no process running outside its critical region may block other processes
• 4. no process should have to wait forever to enter its critical region
Mutual exclusion
• Disabling interrupts
Simplest solution is to have each process disable all interrupts just after entering its critical region
and re-enable them just before leaving it
Demerits
It is unwise to give user processes the power to turn off interrupts because some process disable all
interrupts and never turned them on again?
This scheme will not work for multiprocessor
Conclusion : disabling interrupts is often a useful technique within the Os itself but is not
appropriate as a general mutual exclusion mechanism for user processes
Mutual exclusion
Lock variables
• Consider single shared variable, initially 0
While(lock!=0);
Lock=1
Critical section
Lock=0
Strict alternation
Let integer variable turn is initially zero
Continuously testing a variable until some value appears is called busy waiting. It should be usually
Avoided because it wastes CPU time. A lock that uses busy waiting is called spin lock
Drawback : process running outside critical region may block other processes to enter critical region
Not good idea when one of the process is much slower than the other
Peterson’s solution
#define FALSE 0
#define TRUE 1
#define N 2
int turn; /* whose turn is it? */
int interested[N]; /* all values initially 0 (FALSE) */
leave region:
MOVE LOCK,#0 | store a 0 in lock
RET
Mutual exclusion
• Both Peterson solution and solution using TSL are correct but both have requiring busy
waiting : continuously testing a variable until some value appears . Only when there is a
reasonable expectation that the wait will be short ,busy waiting used. A lock that uses
busy waiting is called a spin lock.
• It should be avoided because it wastes CPU time & it can also have unexpected effects.
• Consider a computer with two processes, H, with high priority, and L, with low priority.
• The scheduling rules are such that H is run whenever it is in ready state. At a certain
moment, with L in its critical region, H becomes ready to run (e.g., an I/O operation
completes). H now begins busy waiting, but since L is never scheduled while H is running,
L never gets the chance to leave its critical region, so H loops forever. This situation is
sometimes referred to as the priority inversion problem.
This can be solved by putting process in block state when it cannot enter critical section. Let
assume that system call sleep and wakeup available
Producer consumer problem
• It is also known as bounded buffer problem
• Two process share a common fixed size buffer
#define N 100 / * number of slots in the buffer */
int count = 0; /* number of items in the buffer */
void producer(void)
{
int item;
while (TRUE) { item = produce item( );
* if buffer is full, go to sleep */
if (count == N) sleep( ); /
item = remove item( ); count = count -1; /* decrement count of items in buffer */
if (count == N - 1) wakeup(producer);
consume item(item);}}
Producer consumer problem
• The buffer is empty and the consumer has just read count to see if it is 0. At that instant, the
scheduler decides to stop running the consumer temporarily and start running the producer.
• The producer inserts an item in the buffer, increments count, and notices that it is now 1.
Reasoning that count was just 0, and thus the consumer must be sleeping, the producer calls
wakeup to wake the consumer up.
• Unfortunately, the consumer is not yet logically asleep, so the wakeup signal is lost. When
the consumer next runs, it will test the value of count it previously read, find it to be 0, and
go to sleep.
• Sooner or later the producer will fill up the buffer and also go to sleep. Both will sleep
forever.
• Simple solution is wakeup waiting bit : When a wakeup is sent to a process that is still awake,
this bit is set. Later, when the process tries to go to sleep, if the wakeup waiting bit is on, it
will be turned off, but the process will stay awake. The wakeup waiting bit is a piggy bank for
storing wakeup signals.
Mutual exclusion
Semaphore
Dijkstra suggested using an integer variable to count the number of wakeups saved for future use
A new variable called a semaphore which is kernel variable. Semaphore could have value 0
indicating no wakeups were saved or some positive value if one or more wakeups were pending
He has also proposed two operations down and up
Down : check value of semaphore if it is zero process is put to sleep
Otherwise decrement it and continue. All done as single indivisible atomic action.
Up : increment the value of semaphore, and wakeup one of the process sleeping on that
semaphore
Again incrementing and waking one process is also indivisible operation
#define N 100 /* number of slots in the buffer */
typedef int semaphore; /* semaphores are a special kind of int */
mutex unlock:
MOVE MUTEX,#0 | store a 0 in mutex
RET |retur n to caller
Here suppose thread do busy waiting then it will loop forever and never
acquired the lock
int min;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
if(mint < min)
min = mint;
pthread_mutex_unlock(&mutex);
Futex
• Spin locks are fast if the wait is short, but waste CPU cycles if not.
• If there is much contention, it is therefore more efficient to block the
process and let the kernel unblock it only when the lock is free.
• it works well under heavy contention, but continuously switching to
the kernel is expensive if there is very little contention to begin with.
• Solution work in both case is : futex, or ‘‘fast user space mutex.’’
• A futex consists of two parts: a kernel service and a user library.
Futex
Futex is taken care in userspace. Then, depending on the value, we can use
the kernel only if we need to sleep or to wake other threads or processes.
futex_lock(unsigned int *futex)
{
• while (!cmpxchg(futex, UNLOCKED, LOCKED))
• futex(futex, FUTEX_WAIT, LOCKED);
•}
• cmpxchg() is an atomic compare-and-exchange operation, where if the
mutex value is UNLOCKED, it replaces it by LOCKED. It returns true if the
operation succeeded or false otherwise
Futex
futex_unlock(unsigned int *futex)
atomic_set(futex, UNLOCKED);
}
Low level synchronization
• Interchange the order of down and up
• It cause dead lock
• Higher level synchronization called monitor proposed by hoare and Hansen
• Monitor is collection of procedures, variables and data structures that all are grouped together in a special kind
of module or package
• Process may call the procedure. Procedure declared inside monitor can access only those variables declared
locally within the monitor and not by any external procedure
• Only one process can be active in a monitor at any instant. Monitor are programming language construct so
compiler knows that they are special and handle calls to monitor procedures differently from other procedure
calls
• When a process call monitor procedure, the first few instructions of the procedure will check to see if any
process is currently active within the monitor
• It is upto the compiler to implement the mutual exclusion on monitor entries, they may use semaphore or mutex
• The compiler, not the programmer is arranging for mutual exclusion, it is much less likely that something will go
wrong
• Implement critical section as procedures of monitor
We also need a way for processes to block when they cannot proceed. In the producer-
consumer problem, it is easy enough to put all the tests for buffer-full and buffer-empty in
monitor procedures, but how
should the producer block when it finds the buffer full?
The solution lies in the introduction of condition variables, along with two operations on them, wait and signal.
When a monitor procedure discovers that it cannot continue (e.g., the producer finds the buffer full), it does a wait on
some condition variable, say, full. This action causes the calling process to block. It also allows another process that
had been previously prohibited from entering the monitor to enter now
This other process, for example, the consumer, can wake up its sleeping partner by doing a signal on the condition
variable that its partner is waiting on.
To avoid having two active process in monitor at the same time, we need a rule telling what happens after a signal
1. Hoarse proposed telling the newly awaked process run, suspending the other one
2. Hansen proposed that a process doing a signal must exit the monitor immediately. Signal statement may appear
as final statement in the monitor procedure
3. Process doing signal continue to run and allow waiting process to run only after that process leave monitor (not
proposed by either)
Imaginary language, Pidgin Pascal
monitor ProducerConsumer
condition full, empty;
integer count;
procedure insert(item: integer);
begin
if count = N then wait(full);
insert item(item);
count := count + 1;
if count = 1 then signal(empty)
end;
procedure consumer;
begin
while true do
begin
item = ProducerConsumer.remove;
consume item(item)
end
end
some real programming languages also support monitors, although not always in the
form designed by Hoare and Brinch Hansen. One such language is Java.
Java is an object-oriented language that supports user-level threads and also allows
methods (procedures) to be grouped together into classes. By adding the keyword
synchronized to a method
declaration, Java guarantees that once any thread has started executing that method,
no other thread will be allowed to start executing any other synchronized method
of that object.
Synchronized methods in Java differ from classical monitors in an essential
way: Java does not have condition variables built in. Instead, it offers two procedures,
wait and notify, which are the equivalent of sleep and wakeup except that
when they are used inside synchronized methods, they are not subject to race
conditions.
public class ProducerConsumer {
static final int N = 100; // constant giving the buffer size
static producer p = new producer( ); // instantiate a new producer thread
static consumer c = new consumer( ); // instantiate a new consumer thread
static our monitor mon = new our monitor( ); // instantiate a new monitor
public static void main(String args[ ]) {
p.star t( ); // star t the producer thread
c.star t( ); // star t the consumer thread
}
static class producer extends Thread {
public void run( ) { // run method contains the thread code
int item;
while (true) { // producer loop
item = produce item( );
mon.inser t(item);}
pr ivate int produce item( ) { ... } // actually produce
}
static class consumer extends Thread {
public void run( ) { run method contains the thread code
int item;
while (true) { // consumer loop
item = mon.remove( );
consume item (item);
}}
pr ivate void consume item(int item) { ... } // actually consume
static class our monitor { // this is a monitor
pr ivate int buffer[ ] = new int[N];
pr ivate int count = 0, lo = 0, hi = 0; // counters and indices
public synchronized void insert(int val) {
if (count == N) go to sleep( ); // if the buffer is full, go to sleep
buffer [hi] = val; // inser t an item into the buffer
hi = (hi + 1) % N; // slot to place next item in
count = count + 1; // one more item in the buffer now
if (count == 1) notify( ); // if consumer was sleeping, wake it up
}
public synchronized int remove( ) {
int val;
if (count == 0) go to sleep( ); // if the buffer is empty, go to sleep
val = buffer [lo]; // fetch an item from the buffer
lo = (lo + 1) % N; // slot to fetch next item from
count = count − 1; // one few items in the buffer
if (count == N − 1) notify( ); // if producer was sleeping, wake it up
retur n val;
}
pr ivate void go to sleep( ) { try{wait( );} catch(Interr uptedException exc) {};}
}
}
IPC problems
• Reader and writer problem
e.g. airline reservation
Many readers and writers
2. Dining philosopher problem
3. The sleeping barber problem
Barber shop has One barber, one barber chair and n chairs for waiting
Dining philosopher problem
Message passing
Shared memory : process running on same machine
• Semaphore or monitor can be used for mutual exclusion on if process
are running on the same machine
• But if process running on different machine then how they
communicate?
Message passing : useful in distributed systems, shared memory
multiprocessor and uniprocessor systems
• Two primitive operations
• Send( destination, &message) and receive(source, &message)
Message passing
Design issues
1. Synchronization : receiver cannot receive message until it has been sent by
some other process. Thus both the sender and receiver can be blocking or
non-blocking. Three combinations are common
a. Blocking send and blocking receive : Both the sender and receiver are
blocked until the message is delivered. this is called rendezvous. It is easier to
implement than a buffered message scheme but is less flexible since the
sender and receiver are forced to run in lockstep
b.Non blocking send and blocking receive : most useful combination.
Processes must employ reply messages (acknowledgment)
c. Non blocking send and non blocking receive
2. Addressing
Direct and indirect addressing
Direct : it include specific identifier of the destination process. Helpful for cooperative
Concurrent processes.
Indirect : not sent directly from sender to receiver, but messages are sent to shared data
structure called mailbox which consists of queues that can temporary hold messages. Mailbox is
a place to buffer a certain number of messages
The relation between sender and receiver may be One to one, many to one or many to many
One to one : private communication link is set up between two processes
Many to one: client/server communication. In this case mailbox is also known as port
One to many : broadcast messages
Many to many : multiple servers and multiple clients
The association of mailbox to process may be static or dynamic
port is static assignment i.e. port is created and assigned to the process permanently
When there are many senders, the association of sender to mail box may be dynamic
In case of dynamic association connect and disconnect operation may required
Port is owned by receiving process. In general mailbox may owned by OS
3.Message format : some preferred short, fix size messages or it can be variable size (header
and body)
4. Queuing discipline : default is FIFO or it can be priority based
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
Barriers
• Some applications are divided into phases and have rule that no
process may proceed into the next phase until all process are ready to
proceed the next phase
• This behavior may be achieved by placing barrier at the end of each
phase. When a process reaches the barrier, it is blocked until all
process have reached the barrier
• int main()
• {
• int sem1, sem2, id, shid;
• int i,pi;
• int array[N];
• float *avg, *dev, x;
•
• for(i = 0; i<N; i++)
• array[1] = 1+1;
•
• shid = shmget(IPC_PRIVATE, sizeof(float)*(PROCESS+1),IPC_CREAT | 0666);
•
• avg = (float *)shmat(shid, 0, 0);
• *avg=0;
• shid = shmget(IPC_PRIVATE, sizeof(float), IPC_CREAT | 0666);
• dev = (float *)shmat(shid, 0, 0);
•
• *dev = 0;
• sem1 = semget(KEY, 2, IPC_CREAT | 0666);
• semctl(sem1, 0, SETVAL, PROCESS+1);
id = process_fork(PROCESS);
sum=0;
for(i=id; i<N; i += PROCESS+1)
{
sum=sum+array[i];
}
down(sem1,1,&s);
*avg = *avg + sum/N;
up(sem1,1,&s);
barrier(sem1);
find deviation
join
print deviation
void waitforzero(int semid, int n, struct sembuf *s)
{
s->sem_num = n;
s->sem_flg = 0;
s->sem_op = 0,
semop(semid, s, 1);
}