Inter-process Communication and Synchronisation
VerifiedAdded on  2023/06/03
|11
|2268
|343
AI Summary
This article discusses the implementation of inter-process communication and synchronization using shared memory, semaphores, and circular buffers. It covers the creation and destruction of shared memory segments, sending single and multiple messages between producer and consumer, variable producer delay, and character-based circular buffer. The article also includes testing and observations on CPU usage and accuracy of read and write into the shared memory.
Contribute Materials
Your contribution can guide someone’s learning journey. Share your
documents today.
Inter-process Communication and Synchronisation
[Name]
Institution
[Name]
Institution
Secure Best Marks with AI Grader
Need help grading? Try our AI Grader for instant feedback on your assignments.
Task 1: Create and destroy a shared memory segment
The producer was implemented by use of a few includes and functions. The main functions
used were
ï‚· ftok; used to generate a unique key for the shared memory segment.
ï‚· shmget : used to create the memory segment and obtain the shared memory ID
ï‚· shmctl used in destroying the shared memory segment.
The producer was tested and confirmed to be running okay, and destroyed the shared memory
segment successfully after the 10 seconds sleep.
Task 2: Send a single message from producer to consumer
Stage 1: Modification of the Producer
The producer made in task 1 was modified to attach a the shared memory segment. This was
done by use of the shmat function as follows: char *sharedMemoryPTR = (char*)
shmat(shared_memory_id,(void*)0,0);
A for loop was then used to set a null on all the memory locations of the shared memory
segment. The MAX_SIZE constant has a value of 32, which is the maximum size of the
shared memory.
for (int c = 0; c <= MAX_SIZE; c++)
*s++ = '\0';
A message was then obtained from the user and set to the memory segment. To obtain the
message the code used was; fgets(message, sizeof message, stdin);
The message was written into the memory segment using the code;
strncpy(sharedMemoryPTR , message,MAX_SIZE );
The producer was implemented by use of a few includes and functions. The main functions
used were
ï‚· ftok; used to generate a unique key for the shared memory segment.
ï‚· shmget : used to create the memory segment and obtain the shared memory ID
ï‚· shmctl used in destroying the shared memory segment.
The producer was tested and confirmed to be running okay, and destroyed the shared memory
segment successfully after the 10 seconds sleep.
Task 2: Send a single message from producer to consumer
Stage 1: Modification of the Producer
The producer made in task 1 was modified to attach a the shared memory segment. This was
done by use of the shmat function as follows: char *sharedMemoryPTR = (char*)
shmat(shared_memory_id,(void*)0,0);
A for loop was then used to set a null on all the memory locations of the shared memory
segment. The MAX_SIZE constant has a value of 32, which is the maximum size of the
shared memory.
for (int c = 0; c <= MAX_SIZE; c++)
*s++ = '\0';
A message was then obtained from the user and set to the memory segment. To obtain the
message the code used was; fgets(message, sizeof message, stdin);
The message was written into the memory segment using the code;
strncpy(sharedMemoryPTR , message,MAX_SIZE );
To detach the shared memory segment, the function shmdt as follows;
shmdt(sharedMemoryPTR);
Finally,, the shared memory segment was destroyed using the function shmctl ;
shmctl(shared_memory_id, IPC_RMID, NULL);
Stage 2: consumer.c
A consumer was created ; to enable the consumer connect to the shared memory segment, a single key
was used for both the producer and the consumer. The key was therefore statically embedded in the
code with a definite value. The consumer connected to the shared memory, read the message in the
memory then detached from the segment. The screenshots below shows both the producer and
consumer running;
shmdt(sharedMemoryPTR);
Finally,, the shared memory segment was destroyed using the function shmctl ;
shmctl(shared_memory_id, IPC_RMID, NULL);
Stage 2: consumer.c
A consumer was created ; to enable the consumer connect to the shared memory segment, a single key
was used for both the producer and the consumer. The key was therefore statically embedded in the
code with a definite value. The consumer connected to the shared memory, read the message in the
memory then detached from the segment. The screenshots below shows both the producer and
consumer running;
Task 3: Send a series of messages between producer and
consumer
Stage 1: Modification of the producer to handle a series of messages
The strategy employed in allowing the producer to handle a series of messages is the use of a
while loop, that runs infinitely until an EOF is encountered. For this case, the inputs are
entered by the user manually, so to send the EOF on Linux, one has to type Ctrl+D which
sends an EOF to the program. Since the program uses fgets to obtain the user inputs from
stdin, the EOF is interpreted as NULL. The code snippet for the loop is as shown below.
while (fgets(message, sizeof message, stdin) != NULL){
//Write the message to the memory segment
strncpy(sharedMemoryPTR , message,MAX_SIZE );
printf("Memory segment contains the message: \%s \n", sharedMemoryPTR);
printf("-Sleeping for 10 seconds!!...\n");
sleep(10);
printf( "Enter a message of up to 20 characters or Ctrl+D to exit:");
}
The screen shot below shows successful testing of the code; the program runs until Ctrl + D
is pressed which indicates EOF.
consumer
Stage 1: Modification of the producer to handle a series of messages
The strategy employed in allowing the producer to handle a series of messages is the use of a
while loop, that runs infinitely until an EOF is encountered. For this case, the inputs are
entered by the user manually, so to send the EOF on Linux, one has to type Ctrl+D which
sends an EOF to the program. Since the program uses fgets to obtain the user inputs from
stdin, the EOF is interpreted as NULL. The code snippet for the loop is as shown below.
while (fgets(message, sizeof message, stdin) != NULL){
//Write the message to the memory segment
strncpy(sharedMemoryPTR , message,MAX_SIZE );
printf("Memory segment contains the message: \%s \n", sharedMemoryPTR);
printf("-Sleeping for 10 seconds!!...\n");
sleep(10);
printf( "Enter a message of up to 20 characters or Ctrl+D to exit:");
}
The screen shot below shows successful testing of the code; the program runs until Ctrl + D
is pressed which indicates EOF.
Secure Best Marks with AI Grader
Need help grading? Try our AI Grader for instant feedback on your assignments.
Stage 2 : Consumer modification to read a series of messages
To facilitate detection of new messages, the available memory address space after address 20
was used. A flag was set at position 26 of the memory. The value of this position was set to 5;
whenever the consumer checks the memory and finds that the value is 5, it knows that no new
messages have been written by the producer. This is because the producer destroys this value
before setting the message on the first 20 locations. This means that whenever the consumer
checks for the value at position 25 and finds that it is not the value of the flag [5], it
immediately indicates that the server has written into the shared memory. The consumer
therefore reads the message.
The consumer also maintains a counter which is incremented by 5 whenever the consumer
finds that the producer has not posted new messengers; If the producer places new messages
the counter is reset to 0, however if no new messages are posted, the counter continues to
increment and if the value of the counter reaches 410 - which is nearly a wait time of 20
seconds, the consumer assumes that the producer is no longer active. It therefore detaches
from the shared memory segment and exits.
char *s = shared_mem;
//the counter is to be used to determine if the producer is no longer available
int counter = 0 ;
for(;;){
if(shared_mem[25] == 5){
counter += 5;
//Bussy Waiting using for loop
for(int i =0 ; i< 100000000; i++);
if(counter > 410 ){
printf("Seems the server is not active \n");
shmdt(shared_mem);
printf("-Detached from Shared Memory \n");
exit(0);
}
}else{
To facilitate detection of new messages, the available memory address space after address 20
was used. A flag was set at position 26 of the memory. The value of this position was set to 5;
whenever the consumer checks the memory and finds that the value is 5, it knows that no new
messages have been written by the producer. This is because the producer destroys this value
before setting the message on the first 20 locations. This means that whenever the consumer
checks for the value at position 25 and finds that it is not the value of the flag [5], it
immediately indicates that the server has written into the shared memory. The consumer
therefore reads the message.
The consumer also maintains a counter which is incremented by 5 whenever the consumer
finds that the producer has not posted new messengers; If the producer places new messages
the counter is reset to 0, however if no new messages are posted, the counter continues to
increment and if the value of the counter reaches 410 - which is nearly a wait time of 20
seconds, the consumer assumes that the producer is no longer active. It therefore detaches
from the shared memory segment and exits.
char *s = shared_mem;
//the counter is to be used to determine if the producer is no longer available
int counter = 0 ;
for(;;){
if(shared_mem[25] == 5){
counter += 5;
//Bussy Waiting using for loop
for(int i =0 ; i< 100000000; i++);
if(counter > 410 ){
printf("Seems the server is not active \n");
shmdt(shared_mem);
printf("-Detached from Shared Memory \n");
exit(0);
}
}else{
//reset counter to show that the producer is still active
counter = 0;
printf("-Message Read from Shared Memory : %s\n",shared_mem);
//set all the memory location to NULL
for (int c = 0; c <= MAX_SIZE; c++)
*s++ = '\0';
//Set flag that will tell the consumer when the segment is changed
shared_mem[25] = 5;
} }
Producer
counter = 0;
printf("-Message Read from Shared Memory : %s\n",shared_mem);
//set all the memory location to NULL
for (int c = 0; c <= MAX_SIZE; c++)
*s++ = '\0';
//Set flag that will tell the consumer when the segment is changed
shared_mem[25] = 5;
} }
Producer
Consumer
Stage 3:
When the producer and consumer were passing messages between one another, it was
observed that the consumer had the highest CPU usage of both processes. The high CPU
usage is attributed to the use of busy wait algorithm by the consumer. For this consumer, a
for loop was used to implement the busy waiting which, although it doesn't do anything
useful , consumes a lot of computing resources. On the other hand, the producer sleeps ;
relieving the CPU to perform other tasks.
Stage 3:
When the producer and consumer were passing messages between one another, it was
observed that the consumer had the highest CPU usage of both processes. The high CPU
usage is attributed to the use of busy wait algorithm by the consumer. For this consumer, a
for loop was used to implement the busy waiting which, although it doesn't do anything
useful , consumes a lot of computing resources. On the other hand, the producer sleeps ;
relieving the CPU to perform other tasks.
Paraphrase This Document
Need a fresh take? Get an instant paraphrase of this document with our AI Paraphraser
Task 4: Variable producer delay
At this stage, the producer was modified to allow the user to control the sleep interval by the
message content. The user's input message in the form of $P:n, where n is a series of digits
representing sleep time in milliseconds, was taken and passed through a number of filtrations
and checks to ascertain its validity.
Nested IF/ELSE structures were used to check if the input was in the valid format and the
value of n was within a range of 800 - 20000. A char array was first used to store the values
of n, which were extracted by use of a for loop.
To start with, the control character was checked and format of the first 3 characters
ascertained using the criteria; message[0] == '$' && message[1] == 'P' && message[2] == ':'
If this condition was met, the value of n was then extracted into an array by use of a for loop; as
shown in the code snippet below;
//Check for control char AND Format
if(message[0] == '$' && message[1] == 'P' && message[2] == ':'){
for(int x = 3; x < sizeof(message);x++){
if (isdigit(message[x]) != 0){
validDigits[x-3] = message[x];
count++;
}else{
break; //the loop breaks at the first instance of a non digit
}//END ELSE
}//END FOR
The array used to store the value of n is then filtered to only extract values that were entered
through message. The value obtained is the sleep time, which has to be converted to seconds
and nano seconds. The code below was used in the conversion.
sleepTime = atoi(filteredValues);
struct timespec req;
/*Since nano sleep takes both seconds
* and nano seconds, the following block
* converts milliseconds to nano seconds
At this stage, the producer was modified to allow the user to control the sleep interval by the
message content. The user's input message in the form of $P:n, where n is a series of digits
representing sleep time in milliseconds, was taken and passed through a number of filtrations
and checks to ascertain its validity.
Nested IF/ELSE structures were used to check if the input was in the valid format and the
value of n was within a range of 800 - 20000. A char array was first used to store the values
of n, which were extracted by use of a for loop.
To start with, the control character was checked and format of the first 3 characters
ascertained using the criteria; message[0] == '$' && message[1] == 'P' && message[2] == ':'
If this condition was met, the value of n was then extracted into an array by use of a for loop; as
shown in the code snippet below;
//Check for control char AND Format
if(message[0] == '$' && message[1] == 'P' && message[2] == ':'){
for(int x = 3; x < sizeof(message);x++){
if (isdigit(message[x]) != 0){
validDigits[x-3] = message[x];
count++;
}else{
break; //the loop breaks at the first instance of a non digit
}//END ELSE
}//END FOR
The array used to store the value of n is then filtered to only extract values that were entered
through message. The value obtained is the sleep time, which has to be converted to seconds
and nano seconds. The code below was used in the conversion.
sleepTime = atoi(filteredValues);
struct timespec req;
/*Since nano sleep takes both seconds
* and nano seconds, the following block
* converts milliseconds to nano seconds
* based on the value of the sleep time
**/
if( sleepTime > 999){
req.tv_sec = sleepTime / 1000;
req.tv_nsec = (sleepTime - ((long)req.tv_sec * 1000)) * 1000000;
}else{
req.tv_sec = 0;
req.tv_nsec = (sleepTime*1000000) * 1000000;
}
The sleep was then initiated using nano sleep as show below;
printf("-Sleeping for %i seconds!!...\n",sleepTime/1000);
nanosleep(&req , NULL);
Stage 2 : Testing with various sleep intervals
Testing was done with a number of sleep intervals; it was observed that the CPU usage for
the producer was more erratic with the use of variable intervals. In some instances the CPU
**/
if( sleepTime > 999){
req.tv_sec = sleepTime / 1000;
req.tv_nsec = (sleepTime - ((long)req.tv_sec * 1000)) * 1000000;
}else{
req.tv_sec = 0;
req.tv_nsec = (sleepTime*1000000) * 1000000;
}
The sleep was then initiated using nano sleep as show below;
printf("-Sleeping for %i seconds!!...\n",sleepTime/1000);
nanosleep(&req , NULL);
Stage 2 : Testing with various sleep intervals
Testing was done with a number of sleep intervals; it was observed that the CPU usage for
the producer was more erratic with the use of variable intervals. In some instances the CPU
usage reduced significantly while in some instance - especially when short sleep intervals
were used - the usage increased as compared to when a constant sleep value was used.
Starting the consumer after the producer had been running for a while did not have any major
effect; the consumer and producer were synchronized , this is mainly due to the fact that there
was room for only one message. However, when shorter sleep time were used. The consumer
fell out of sync with the producer and showed signs of missing out some messages.
Task 5: Implement semaphores
Stage 1: Replace Busy waiting with semaphores
To replace the busy wait with a semaphore, modification were done on both the producer and
the consumer. Since the shared location has to be accessed by both the producer and
consumer; the semaphore was created by the producer using a key that was then stored in a
file. The producer accessed the file and obtained the key, allowing it to connect to the
existing semaphore. This approach made it possible to synchronize access to the shared
resource.
The implementation of the semaphores had a significant positive effect in terms of CPU
usage. By comparison to the busy wait; the semaphore resulted in minimal use of the CPU.
The producer was modified to first check if the value at position 30 is set to 5; which
indicates that the consumer has accessed the shared memory.
Stage 2
Top command was used to observed CPU utilisation by the producer and consumer. It was
observed that with the use of the semaphore, the consumer used less CPU time than when
using the busy wait. By comparison to results obtained from the other tasks, the use of
semaphore
were used - the usage increased as compared to when a constant sleep value was used.
Starting the consumer after the producer had been running for a while did not have any major
effect; the consumer and producer were synchronized , this is mainly due to the fact that there
was room for only one message. However, when shorter sleep time were used. The consumer
fell out of sync with the producer and showed signs of missing out some messages.
Task 5: Implement semaphores
Stage 1: Replace Busy waiting with semaphores
To replace the busy wait with a semaphore, modification were done on both the producer and
the consumer. Since the shared location has to be accessed by both the producer and
consumer; the semaphore was created by the producer using a key that was then stored in a
file. The producer accessed the file and obtained the key, allowing it to connect to the
existing semaphore. This approach made it possible to synchronize access to the shared
resource.
The implementation of the semaphores had a significant positive effect in terms of CPU
usage. By comparison to the busy wait; the semaphore resulted in minimal use of the CPU.
The producer was modified to first check if the value at position 30 is set to 5; which
indicates that the consumer has accessed the shared memory.
Stage 2
Top command was used to observed CPU utilisation by the producer and consumer. It was
observed that with the use of the semaphore, the consumer used less CPU time than when
using the busy wait. By comparison to results obtained from the other tasks, the use of
semaphore
Secure Best Marks with AI Grader
Need help grading? Try our AI Grader for instant feedback on your assignments.
Task 6: Character-based circular buffer
Stage 1 : Circular buffer
The producer was modified to write character by character to the buffer instead of the
complete line as it was in task 5. To write the data to the buffer, a for loop was used, which
iterated over the char array picking each character and pushing it to the end of the buffer.
When a new value was written into the buffer by the producer, every existing value was
pushed ahead; effectively creating a FIFO structure where new values at the end of the
buffer pushes out the older values.
Stage 2: Character-based sleep control message processing
To implement character-based control on sleep time of both the producer and the consumer,
the first three characters of any messages were read and then each character tested. The first
task was to identify the occurrence of the control character $, then determining if the message
is for the producer or consumer; finally the characters after the colon were tested if they are
digits using the isDigit function. The default sleep interval was set at 100ms for both the
producer and the consumer.
Stage 3 Testing
Testing was done using a specially designed test scrip which tried to check if the variation of
sleep time between the producer and the consumer affected CPU usage and accuracy of the
read and write into the shared memory. Test showed that the program performed as expected.
The use of semaphore ensured that no message could be overwritten before the consumer
read the message.
Stage 1 : Circular buffer
The producer was modified to write character by character to the buffer instead of the
complete line as it was in task 5. To write the data to the buffer, a for loop was used, which
iterated over the char array picking each character and pushing it to the end of the buffer.
When a new value was written into the buffer by the producer, every existing value was
pushed ahead; effectively creating a FIFO structure where new values at the end of the
buffer pushes out the older values.
Stage 2: Character-based sleep control message processing
To implement character-based control on sleep time of both the producer and the consumer,
the first three characters of any messages were read and then each character tested. The first
task was to identify the occurrence of the control character $, then determining if the message
is for the producer or consumer; finally the characters after the colon were tested if they are
digits using the isDigit function. The default sleep interval was set at 100ms for both the
producer and the consumer.
Stage 3 Testing
Testing was done using a specially designed test scrip which tried to check if the variation of
sleep time between the producer and the consumer affected CPU usage and accuracy of the
read and write into the shared memory. Test showed that the program performed as expected.
The use of semaphore ensured that no message could be overwritten before the consumer
read the message.
1 out of 11
Related Documents
Your All-in-One AI-Powered Toolkit for Academic Success.
 +13062052269
info@desklib.com
Available 24*7 on WhatsApp / Email
Unlock your academic potential
© 2024  |  Zucol Services PVT LTD  |  All rights reserved.