Sunteți pe pagina 1din 30

Interprocess Communication

and Synchronization
It is often necessary for individual processes or threads a single process to
collaborate. For this to be achieved, they need facilities to support
communication and coordination (synchronization) so that errors do not
occur.
The operating system provides these services to processes and also uses
them itself, since it is also a multiprogramming application.
One of the compelling reasons for using threads is that inter-thread
communication via shared global memory is very simple and OS provided
interprocess communication (IPC) facilities are not needed. However, the
threads must use locking mechanisms to coordinate access to the shared
memory. We will see that it is not possible for a user level program to
correctly implement these locks. Thus it is necessary that the operating
system provides synchronization facilities.
Interprocess Communication
Examples of interprocess and interthread communication facilities includes:
Datatransfer:
Pipes (named, dynamic shell or process generated)
shared buffers or files
TCP/IP socket communication (named, dynamic - loop back interface or network interface)
D-Bus is an IPC mechanism offering one to many broadcast and subscription facilities between processes. Among
other uses, it is used by graphical frameworks such as KDE and GNOME. See the wiki.
Sharedmemory
Between Processes
Between Threads (global memory)
Messages
OS provided message passing:
send() / receive() see Microkernels
Signals
Via locks, semaphores or monitors
Handling Signals
Signals other that SIG_KILL and SIG_STOP may be caught and
execute a defined signal handler function.
Potential Interprocess Communication Problems
Interprocess communication (IPC) requires the use of resources,
such as memory, which are shared between processes or threads. If
special care is not taken to correctly coordinate or synchronize
access to shared resources, a number of problems can potentially
arise.
Deadlock
A deadlock condition can occur when two processes need multiple
shared resources at the same time in order to continue.
Starvation
A starvation condition can occur when multiple processes or
threads compete for access to a shared resource. One process may
monopolise the resource while others are denied access.
Data Inconsistency
When shared resources are modified at the same time by multiple
resources, data errors or inconsistencies may occur. Sections of a
program that might cause these problems are called critical sections.
Shared Buffer Problem
An example of data inconsistency that can occur because of a race condition
is what can happen with a shared bank account.
Synchronization Facilities
The solution to cooperating processes sharing resources without
encountering the Potential Interprocess Communication Problems is to take
advantage of Operating System provided synchronization facilities.
Synchronization Requirements
The shared buffer problem and most synchronization problems
require mutual exclusion: Only one process or thread can be in the
critical section at a time.
Possible Synchronization Solutions :
Disable Interrupts
To execute the critical section:
This block interrupts for the whole computer
Interrupts could be disabled arbitrarily long
Semaphore
Dijkstra's Semaphore
Conceptual OS mechanism, with no specific implementation defined (could be enter()/exit())
Basis of all contemporary OS synchronization mechanisms
A simple hardware based solution
A semaphore, s, is a nonnegative integer variable that can only be changed or tested by these two
indivisible functions:
V(s): [s = s + 1]
P(s): [while(s == 0) {WAIT()}; s = s - 1]
V and P are the first letters of Dutch words. P (probern to test) is used for acquiring the lock. V
(verhogen to increment) is used for releasing the lock.
The square brackets [ ] here mean that the actions between the brackets is to be autonomous
(not interruptible).
The initial value of s determines how many simultaneous threads may hold the semaphore lock. Set s to 1 for a
simple lock, called a binary lock or binary semaphore. Some problems allow for s to be larger than 1.
The Monitor
The semaphore provides a foundation for implementing solutions to
synchronization problems. Unfortunately, finding a completely correct
solution to some of the more challenging problems using semaphores
is quit difficult. Thus, many programming environments use
semaphores, or the same facilities used to implement semaphores, to
provide an alternative called monitors, which for some problems
allows for a simpler solution. Monitors are also often called
conditional waits or conditional monitors.
The conditional monitor object contains an internal mutual exclusion
lock and methods for a process to wait() until another thread issues a
notify() method to awake the thread. Correct use of monitors is fairly
simple, but generally calls for following a fixed programing model.
The key to correctly using a monitor is to always put the wait()
operation inside a while loop as shown below. The monitor s wait()
method releases the held lock so that other threads are not held up,
but reacquires it when exiting the waiting state. Thus, the check to
enter the critical state (the negative of the while loop boolean
expression), is always done with mutual exclusion. The monitor
allows the programmer to simply express the criteria for entering the
critical section in terms of a boolean expression, which generally
simplifies the solution to many problems.
/* Code to enter a critical section */
monitor.acquire();
while(not_okay_to_enter())
monitor.wait();
keep_others_out();
monitor.release();
/*---------------------------------*/
/*CriticalSection*/
/*---------------------------------*/
/*Exitofcriticalsectioncode*/
monitor.acquire();
okay_others_to_enter();
monitor.notify();
monitor.release();
Multi-threaded Programming
Modern programming languages do vary slightly in how threads of a
process are created, managed and synchronize access to critical
sections.
C/C++, Java and Python all provide the needed facilities.
We will mostly look at Python s facilities, which is quite straight
forward.
Python provides synchronization facilities very similar to C/C++,
except at the object level. Pythons synchronization objects take care
of most of the lower level details for us. Yet, it still provides a great
deal of flexibility to the programmer.
Creating threads in Python
Threads are created via the threading.Thread class from the threading module. There are three ways that this can
be accomplished.
Create Thread instance, passing in a function
Create Thread instance, passing in a class
Subclass Thread and create subclass instance
class threading.Thread(target, args)
Parameters:
target (callable function) identifies the code (function) for the new thread to execute
args (list) is the arguments to pass to the target function
setDaemon(n)
Manage the persistence of the child thread relative to the parent. n of True or 1 means that the child thread dies if the parent dies first.
n of False or 0 means that the child thread can keep running after parent is finished.
start()
Begin execution of the thread now.
join()
The current (parent) thread should suspend until the child thread terminates.
example
importthreading
#...
t=threading.Thread(target=threadcode,args=[arg1,arg2])
t.setDaemon(1)
t.start()
#...
t.join()
Synchronization tools
Pythons Simple Lock
classthreading.Lock
A simple mutual exclusion lock used to limit access to one thread. This
is a semaphore with s = 1.
acquire()
Obtain a lock. The process is blocked until the lock is available.
release()
Release the lock and if another thread is waiting for the lock, wake up
that thread.
Here is how to provide mutual exclusion access to a critical section:
importthreading
L=threading.Lock()
L.acquire()
#Thecriticalsection...
L.release()
Pythons Semaphore
classthreading.Semaphore(s)
A general purpose lock used to limit access to s threads (Lock and
Semaphore are almost the same) the default value of s is 1. (see
Dijkstras Semaphore)
acquire()
Obtain a semaphore. The process is blocked until the semaphore is
available.
release()
Release the semaphore and if another thread is waiting for the
semaphore, wake up that thread.
The following code allows up to five threads in the critical section at
one time:
importthreading
S=threading.Semaphore(5)
S.acquire()
#
#Thecriticalsection...
#
S.release()
Pythons Conditional Monitor
classthreading.Condition
Implementation of a monitor. (See The Monitor) It allows a thread to wait and be
signaled by another thread based on some condition possibly becoming True.
acquire()
Obtain the monitors internal lock. The process is blocked until the lock is available.
release()
Release the monitors internal lock and if another thread is waiting for the lock, wake up that thread.
wait()
Release the underlying lock, and then block until it is awakened by a notify() or notifyAll() call for the
same condition variable in another thread. Once awakened, it re-acquires the lock and returns.
notify()
Wake up one of the threads waiting for the condition variable, if any are waiting.
notifyAll()
Wake up all of the threads waiting for the condition variable, if any are waiting.
The Condition class provides a level of abstraction, which can greatly
simplify the solution to many problems.
Notice that the conditional wait() statement should always be used
inside a while loop. This ensures that the logical condition is still true
before the thread enters the critical section.
It could be that another thread saw a condition, which prompted it to
issue a notify() statement, but by the time our thread returned from
wait(), the condition is no longer true.

S-ar putea să vă placă și