Professional Documents
Culture Documents
programming language.
Solution:
Analysis
The "pick up chopsticks" part is the key point. How does a philosopher pick up
chopsticks? Well, in a program, we simply print out messages such as ``Have left
chopsticks'', which is very easy to do. The problem is each chopstick is shared by two
philosophers and hence a shared resource. We certainly do not want a philosopher
to pick up a chopstick that has already been picked up by his neighbor. This is a
race condition. To address this problem, we may consider each chopstick as a
shared item protected by a mutex lock. Each philosopher, before he can eat, locks
his left chopstick and locks his right chopstick. If the acquisitions of both locks are
successful, this philosopher now owns two locks (hence two chopsticks), and can eat.
After finishes easting, this philosopher releases both chopsticks, and thinks! This
execution flow is shown below.
Because we need to lock and unlock a chopstick, each chopstick is associated with a
mutex lock. Since we have five philosophers who think and eat simultaneously, we
need to create five threads, one for each philosopher. Since each philosopher must
have access to the two mutex locks that are associated with its left and right
chopsticks, these mutex locks are global variables.
Program
Let us see how the above analysis can be convert to a program. Since each
philosopher should be run as a thread, we define a Philosopher class as a derived
class of class Thread.
#include "ThreadClass.h"
#define PHILOSOPHERS 5
};
The constructor takes two arguments, Number for the number assigned to this
philosopher thread, and iter for specifying the number of thinking-eating cycles.
The implementation of this class, as shown below, should have all thinking, eating,
locking and unlocking mechanisms implemented. Since each chopstick must be
protected by a mutex lock, we declare an array Chopstick[ ] of pointers to Mutex.
Since the main program allocates these locks, they are declared as global variables
using extern.
Function Filler() generates a char array that contains some spaces. Note that this
function is declared to be static so that is can only be used within this file (i.e.,
Philosopher.cpp).
Let us look at the constructor. It receives two arguments. The first, Number, is
assigned by the main program to indicate which philosopher this thread represents.
The second, iter, gives the number of thinking-eating cycles for each philosopher.
The constructor is very simple. It gives this thread a name. Thus, if the value of
Number is 2 (i.e., philosopher 2), this thread will have the name Philosopher2.
#include <iostream>
#include "Philosopher.h"
return Space;
}
void Philosopher::ThreadFunc()
{
Thread::ThreadFunc();
strstream *Space;
int i;
Space = Filler(No*2);
Let us look at how locking and unlocking of chopsticks is carried out. Suppose the
chopsticks are numbered counter clockwise. For philosopher i, his left chopstick is i
and his right chopstick is i+1. Of course, we cannot use i+1 directly, because when
i=4, the right chopstick of philosopher is 0 rather than 5. This can easily be done
with the remainder operator: (i+1) % PHILOSOPHERS, where PHILOSOPHERS is the
number of philosophers. In the above code, philosopher No thinks for a while, locks
his left chopstick by calling the method Chopstick[No]->Lock(), locks his right
chopstick by calling the method Chopstick[(No+1) % PHILOSOPHERS]->Lock(), eats
for a while, unlocks his left chopstick by calling the method Chopstick[No]-
>Unlock(), and unlocks his right chopstick by calling the method Chopstick[(No+1)
% PHILOSOPHERS]->Unlock(). This completes one thinking-eating cycle. This cycle
repeats for Iteration number of times.
Note that in the above code each philosopher picks up, or locks, his left
chopstick first followed by the right one.
The main program, as usual, is easy as shown below. The number of thinking-eating
cycles a philosopher must perform is the only command line argument. Since
mutex locks must be created before their uses, the main program allocates
the mutex locks before the creation of threads. In the following, each mutex
lock is created with a name like ChopStick0, ChopStick1, ..., ChopStick4. After all
chopstick locks are created, the main thread continues to create philosopher threads
and joins with all of its child threads. When all philosopher threads terminate, the
main thread returns (i.e., terminates).
#include <iostream>
#include <stdlib.h>
#include "Philosopher.h"
if (argc != 2) {
cout << "Use " << argv[0] << " #-of-iterations." << endl;
exit(0);
}
else
iter = abs(atoi(argv[1]));
for (i=0; i < PHILOSOPHERS; i++) { // initialize and run philosopher threads
Philosophers[i] = new Philosopher(i, iter);
Philosophers[i]->Begin();
}
Exit();
return 0;
Discussion
Ques 2: Study and implement the Dekker’s solution to mutual exlusion using
C/C++ programming language.
Mythical Mutex
Preventing race conditions requires the ability to protect critical regions of code -
areas of code performing updates of a variable that is shared between threads must
allow only one thread to enter one of these regions at a time. We need an operator
that will allow only one thread to enter any one of regions at a time, suspending any
others that try until the first has completed its task, and left the critical region.
A critical region is a code segment that, with respect to similar critical regions is (or
should be) executed atomically. Variables that are shared amongst the processes are
manipulated in the critical region, and no process must be allowed to change them
while any other process is manipulating these variables.
Simply put, a critical region is one in which shared variables can be changed.
If two threads try to write different values to the same memory word at the same
time, the net result will be one of the two values, not some combination of the
values. Similarly, if one CPU tries to read a memory word at the same time another
modifies it, the read will return either the old or new value--it will not see a ``half-
changed'' memory location.
Lock:
which consists of all the steps required before the critical region is safe to enter.
UnLock:
which consists of the actions require after the critical region, to allow other processes
to gain their own lock on the region.
Mutual exclusion then becomes associated with critical regions. Each critical region
needs to be surrounded by a lock-unlock pair, and one common mutex operator
must be used by all processes desiring to enter the critical region. The specifics of
the lock and unlock operators are unimportant in terms of the abstract functionality
provided by the mutex.
Mutex operator may exist as part of the library of functions provided by the
operating system, or can be created using atomic primitives supported by the
microprocessor. Mutexes can also be created by careful manipulation of shared
variables, or by using other synchronization operators provided by the operating
system. Mutexes can also be built into more complex synchronization operators that
offer additional functionality or ease of use in some situations.
This is the first correct solution proposed for the two-process case. Originally
developed by Dekker in a different context, it was applied to the critical section
problem by Dijkstra.
Both the turn variable and the status flags are combined in a careful way. The entry
protocol begins as in Solution 3; we (the requesting process) set our flag and then
check our neighbor's flag. If that flag is also set, the turn variable is used. There is
no progress problem now because we know the other process is in, or before its
critical region. If the turn is ours we wait for the flag of the other process to clear. No
process will wait indefinitely with its flag set. If the turn belongs to the other process
we wait, but we clear our flag before waiting to avoid blocking the other process.
When the turn is given to us we reset our flag and proceed.
Global boolean array variable flag with two components both initialized to false;
flag[i] = true;
if (turn != i)
flags[i] = false;
flags[i] = true;
critical region
turn = 1 - i;
flags[i] = false;
ANALYSIS: The mutual exclusion requirement is assured. No process will enter its
critical region without setting its flag. Every process checks the other flag after
setting its own. If both are set, the turn variable is used to allow only one process to
proceed. .
The turn variable is only considered when both processes are using, or trying to use,
the resource. Deadlock is not possible. No process waits with its flag continuously
set, and one process always has the turn. The process with the turn will (eventually)
discover the other flag free and will proceed.
Finally, bounded waiting is assured. Suppose Pj exits its critical region and re-enters
immediately while Pi is waiting. Then the turn has been given to Pi , and the flag of
Pi is set. Pj will clear its flag and wait, and Pi will proceed.
The mutex class constructed from this successful solution would look like:
class Mutex
{
boolean flag[2]
int turn;
Mutex ()
{
flag[0] = false;
flag[1] = false;
turn = 0;
}
Figure
Using such type of multiple 2X2 switches as building blocks. It is possible to build a
multistage network to control the communication between multiple sources and
destination . These can be connected to each other, either in the form of a binary
tree (Figure 7) or omega network ( figure 8). To understand the working of such
type of systems , consider the binary tree of figure 7. The two processors P1 and P2
are connected through switches to eight memory modules marked in binary from
000 to 111. The path from the source to the destination is determined by the binary
bits of destination . The first bit of destination number determines the switch output
in the first level. The second bit specifies the output of the switch in the second level
and so on. For example, to connect P1 to memory 101, it is necessary to form a path
from P1 to output 1 in the first level switch, output 0 in the second level switch and
output 1 in the third level switch p1 and P2 can be connected to any of the eight
memory modules. Certain request patterns cannot be satisfied simultaneously. For
example, if P1 is connected to one of the destination 000 through 011 then P2 can
be connected to only of the destination from 100 to 111.
Figure
The processor and the memory can be interconnected in number of ways. One such
topology is the omega switching network( fig 8) In this there is exactly one path
from any one processor to any memory module. However two sources cannot be
connected simultaneously to destination 000 and 001 and 011 and so on. The path
from source to the destination is determined is determinded in the same way as in
previous case. The source send a 3 bit pattern representing the destination number.
This binary pattern moves through network, and is examined by the junction boxes
one by one. Level 1 examines the first bit of the pattern and depending on whether it
is 0 or 1 , routes it to upper or lower output. Through one of the outputs pattern now
moves to seond level. The seond level examines second bit of the pattern and
accordingly moves pattern through appropriate out put to third level, which repeats
process with third bit of pattern.
Figure
The configuration can be used for both loosely coupled and tightly coupled system.
For tightly coupled systems, the source and the destination are the processing
elements.
Ques 4. Develop a procedural design (algorithm nearer to C-language code) for nay
encryption / decryption of your choice given in the book. Define all appropriate data
structure in the design representation.
Solution:
Rivest-Shamir-Adelman (RSa) public-key algorithm
The invention of public key encryption in the 70s has greatly improved the practical
usability of encryption, it allows for the secure exchange of information between two
parties with out need to exhange secret keys whih has always been a problem with
conventional encryption scheme.
Public keys schemes solves this shortfall by having two keys , one to encrypt, and
another to decrypt. Messages encryted with public key an only be decryted by
corresponding private key. This removes need for secure key does is allow someone
to encrypt a message for owner of the key pair.
The RSA public key algorihm was published in ACM in 1979, by researchers at MIT,
Adi shamir has sine returned to Israel , where he continues topursue his research in
cryptography The security of the RSA crypto system is based on the presumption
that fatorizing algo since RSA paper was published , these advances have been easily
compensated for by increasing key sizes. RSA key sizes of 2048 or more bits are
considered to be extremely secure at present Part of the RSA systems strength is the
difficulty of factorizing RSA modulus increases exponentially with increases in key
sizes.
This algorithm a clever design , but math involved is elegantly simple. The
encryption / decryptiom algorithm is very simple, and the key generation moderately
easy to follow. RSA key generation produces three numbers: n ( the modulus), e
(encryption key ) and d (decryption key ).
It’s that simple, there are various optimizations to speed up the calculation that this
is essentially what is going on in RSA encryption. To decrypt the message , you
reverse the process with cipher text C and using the derytion key d as exponent.
d
M= C mod n
Users A, B … can create their own pairs (Ea, Da) , (Eb,Db) … of RSA key pairs.
Encryption algo are published or made available ona secure public key server, while
the decryption algorithm are kept secret from everyone except the originator. The
previous section has gone over how these can be used.
In RSA , the plaintext and ciphertext are just large positive integers, up to certain
size depending on specific key pair. The underlying algorithm are not secret but only
certain information used in them.
The RSA the plaintext and ciphertext are just large positive integers , up to certain
size depending on specific key pair. The underlying algorithm are not secret but only
certain information used in them.
1. Choose random “large” prime integers p and q of roughly the same size , but
not too close together.
2. Calculate the product n = p*q (ordinary integer multiplication)
3. Choose a random encryption exponent e less than n that has no fators in
common with either p-1 or q-1
4. Calculate the (unique) decryption exponent satisfying e*d mod (p-1)*(q-1) =
1
5. The encryption function is
e
E(m) = M mod n, for any message m.
d
6. The decryption function is D(c ) =c mod n, for any ciphertext c.
7. The public key (published): This is the pair of integers ( n, e)
8. The private key ( kept secret ) : This is the triple of intgers (p, q, d)
Some people are surprised that RSA just deals with large integers . So how does
it represent data . Suppose the value of n is at least 1024 bits long. This is the
same as 128 bytes In priniciple , one can just run encrypted or signed In
practice, the protocols will demand additional data besides just raw message ,
such as timestamps , but there is room for a lot of data in single RSA encryption.
David also must make sure that the encryption key must be relatively
prime, or has nocommonfactor with the numbers.
(a-b)(b-1) = 46 X 70 = 3220
David then chooses79 for his encryption key, using theextended Eulid
algo. And prime numbers (which are known only to David) , he calculates
the decryption key.
D = 79 ^-1(mod(3220) = 1019
David then publishes his public keyonhis web site for people to encrypt
messages with to send to him.
David’s RSA
key
C =
3337
Encryption key= 79
The next day Antonia wants to encrypt the number 688 and raises it to
the 79th power and takes the modulo 3337 of the result
(688^79)mod (3337) = 1570
David then gets the number 1570, and tries to decrypt it by replacing the
encryption with the decryption key.
(1570^1019)mod (3337) = 688
Rivest , Shamir abd Adelman wrote a paper descirbing the invention and published it
as MIT artificial Intelligence Laboratory Technical Report . A challenge was placed in
Martin Gardner column in Scientific American in which readers were invited to crack.
C=114, 381, 625, 757, 888,867, 669, 235, 779 , 976, 146, 612, 010, 218, 296, 721,
242
,362, 562,561,842,935,706,935,245,733,897,830,597,123,563,958,705,058,989,
075,147,599,290,026,879,543,541
Encryption key = 9007
The message “first solver wins one hundred dollar” appeared in an A=01, B=02,
C=03… format. This was solved in April 26, 1994, cracked by an international
effort vis the internet with the use of 1600 workstations, mainframes and
supercomputer attacked the number for eight months before finding its factors.
Of course, RSa algorithm is safe, as it would be incredibly difficult to gather up
such international participation to commit malicious acts.
Despite the brilliant idea , RSA didn’t sell very well , this is where PGP comes in,
people needed a practical , affordable demonstration that they could try
themselves.