The goal of this assignment is to become familiar with concurrent processing in
ID: 3834557 • Letter: T
Question
The goal of this assignment is to become familiar with concurrent processing in Unix/Linux using shared memory.
You will write a program that uses multiple processes to simulate a swim mill to show the behavior of a fish swimming upstream. The swim mill is represented as a one-dimensional array such that each element indicates the distance from the shore. The fish will occupy one and only one of these elements. The presence of a fish is indicated by changing the integer in the specified element. For example, we can represent the fish by the character F. By default, we will have the fish swimming right in the middle of the stream.
Somewhere upstream, a pellet is dropped into the stream. Our fish sees this pellet traveling towards it and moves sideways to enable its capture. If the pellet and the fish are in the same position at any point, the fish is said to have eaten the pellet. Of course, it is possible for our fish to miss the pellet if the fish cannot move close to it in time.
Different types of processes needed for this project are as follows:
•A set of pellet processes: Each pellet process drops a pellet at a random distance from the fish. We will call this process pellet. The pellet will start moving towards the fish with the flow of the river. For simplicity, we will assume that the pellet moves in a straight line downstream.
•A process called fish: the fish sees the pellet as it is dropped and moves one unit left or right to start aligning itself with the pellet. As the pellet moves towards the fish and the fish is at the same distance from the shore as the pellet, it stops changing its sideways position and devours the pellet when they are coincident. After eating the pellet, or if the pellet is missed, the fish returns to swimming midstream.
•A coordinator process: It is responsible for creating the fish process, the pellet processes, and coordinating the termination of pellets. We could have more than one pellet at any time. Note that the position of pellet and fish are maintained by their respective processes.
The coordinator process also sets a timer at the start of computation to 30 seconds. If computation has not finished by this time, coordinator kills all the children and grandchildren, and then exits. Make sure that you print appropriate message(s).
In addition, coordinator should print a message when an interrupt signal `(^C)` is received. Make sure that all the children/grandchildren are killed by coordinator when this happens, and all the shared memory is deallocated. The grandchildren kill themselves upon receiving interrupt signal but print a message on stderr to indicate that they are dying because of an interrupt, along with the identification information. Make sure that the processes handle multiple interrupts correctly. As a precaution, add this feature only after your program is well debugged.
Implementation:
The code for pellet, fish, and coordinator processes should be compiled separately and the executables be called pellet, fish, and swim_mill, respectively. The program should be executed by calling: swim_mill
Each pellet process prints its process id, its position and whether it was eaten or missed, before exiting. Each time the coordinator gets a result from a pellet, it prints the pid of the pellet.
The coordinator will set up the two-dimensional array in shared memory, and write the result into a file after each child is finished. The fish will be restricted to move in the last row of the 2D array. The swim_mill process will create a pellet at random intervals, with the pellet being dropped into the swim mill at a random distance from the fish. The process pellet will increment its position and will terminate after the pellet has reached the last row, whether it is eaten or not.
The pellet can be represented by 0x80 and can be moved to location (x,y) by the statement L[x][y] |= 0x80. This will allow the pellet and the fish to be able to coincide. The process of eating is performed by getting rid of the most significant bit to return the fish to original state.
The process fish will scan the entire array and will focus on a pellet if it detects one, to arrange itself in its line of drift. Meanwhile, if another pellet is found that happens to be closer, and eatable, the fish will go for it.
Other points to remember: You are required to use fork, exec (or one of its variants), wait, and exit to manage multiple processes. Use shmctl suite of calls for shared memory allocation. Also make sure that you do not have more than twenty processes in the system at any time. You can do this by keeping a counter in swim_mill that gets incremented by fork and decremented by wait.
I’m also requiring the use of a makefile for your compilation. It will save you a lot of time compiling and re-compiling all three source code files and is very common for source code downloaded from FOSS projects and larger-scale, complex coding. A make file i a simple script that contains all of the compilation executions and the options for each file’s compilation.
Hints:
You will need to set up shared memory in this project to allow the processes to communicate with each other. Please check the man pages for shmget, shmctl, shmat, and shmdt to work with shared memory.
You will also need to set up signal processing and to do that, you will check on the functions for signal and abort. If you abort a process, make sure that the parent cleans up any allocated shared memory before dying. In case you face problems, please use the shell command ipcs to find out any shared memory allocated to you and free it by using ipcrm.
You may also want to break down this assignment into parts in order to make it more manageable. First, focus on creating a coordinator process that sets up and can read and write to a shared memory location. Second, focus on getting a fish process that reads/writes to shared memory and can update its own location. Third, work on the pellet and its creation of random locations (pellet drops) and have it decrement its location through the shared memory. Finally, work on getting all three processes to coordinate and work in sync.
Explanation / Answer
Fish.c
#define _XOPEN_SOURCE 700
#include <stdlib.h> // Standard library
#include <time.h> // For seeding rand
#include <unistd.h> // For fork
#include <signal.h> // For signal
#include <stdbool.h> // For bool
#include <stdio.h> // Standard I/O
#include <sys/types.h> // For types (such as pid_t)
#include <sys/ipc.h> // For interprocess communication
#include <sys/shm.h> // For shared memory
#include <sys/wait.h> // For wait
void onInterrupt();
void onTermination();
int main(int argc, char *argv[]) {
signal(SIGINT, onInterrupt);
signal(SIGTERM, onTermination);
int sMemID; // Shared memory ID
key_t keyValue = 1337;
char (*dataPtr)[10][10]; // Pellet 2D Array
// Create shared memory segment. Exit on error.
if ((sMemID = shmget(keyValue, sizeof(char[10][10]), 0666)) < 0) {
perror("Could not create memory segment! ");
exit(1);
}
// Attach the shared memory segment. Exit on error.
if ((dataPtr = (char(*)[10][10])shmat(sMemID, NULL, 0)) == (char(*)[10][10]) -1) {
perror("Could not attach shared memory segment! ");
exit(1);
}
*dataPtr[9][5] = 'F';
while(1) {
// Get location of the fish
int fishCurrentCol;
for (int i = 0; i < 10; i++) {
if (*dataPtr[9][i] == 'F') {
fishCurrentCol = i;
//printf("Fish location: %d ", fishCurrentCol);
}
}
// Check closest pellet row based on location of the fish
int closestPelletRow = 10;
for (int i = 9; i >= 0; i--) {
for (int j = 9; j >= 0; j--) {
if (*dataPtr[i][j] == 0x50 && abs(closestPelletRow - 9) < abs(i - 9)) {
// Pellet found!
closestPelletRow = i;
printf("Closest row: %d ", closestPelletRow);
}
}
}
// Check closest pellet column based on location of the fish
int closestPelletCol = 10;
for (int i = 9; i >= 0; i--) {
for (int j = 9; j >= 0; j--) {
if (*dataPtr[i][j] == 0x50 && abs(closestPelletCol - fishCurrentCol) > abs(j - fishCurrentCol)) {
// Pellet found!
closestPelletCol = j;
printf("Closest column: %d ", closestPelletCol);
}
}
}
if (closestPelletCol != 10) {
// Now move the fish!
// Calculate distance
int distanceCol = closestPelletCol - fishCurrentCol;
while (abs(distanceCol)) {
bool breakTestRow = false;
// Check closest pellet row based on location of the fish
int closestPelletRow = 10;
for (int i = 9; i >= 0; i--) {
for (int j = 9; j >= 0; j--) {
if (*dataPtr[i][j] == 0x50 && abs(closestPelletRow - 9) < abs(i - 9)) {
break;
breakTestRow = true;
}
if (breakTestRow) {
break;
}
}
}
bool breakTestCol = false;
// Check closest pellet column based on location of the fish
int closestPelletCol = 10;
for (int i = 9; i >= 0; i--) {
for (int j = 9; j >= 0; j--) {
if (*dataPtr[i][j] == 0x50 && abs(closestPelletCol - fishCurrentCol) > abs(j - fishCurrentCol)) {
break;
breakTestCol = true;
}
if (breakTestCol) {
break;
}
}
}
*dataPtr[9][fishCurrentCol] = '~';
if (distanceCol > 0) {
fishCurrentCol++;
}
else {
fishCurrentCol--;
}
*dataPtr[9][fishCurrentCol] = 'F';
if (distanceCol > 0) {
distanceCol--;
}
else {
distanceCol++;
}
struct timespec s;
s.tv_sec = 0;
s.tv_nsec = 500000000;
//nanosleep(&s, NULL);
sleep(1);
}
}
else {
// Move the fish back to the middle!
// Calculate distance
int distanceCol = fishCurrentCol - 5;
while (abs(distanceCol)) {
bool breakTestRow = false;
// Check closest pellet row based on location of the fish
int closestPelletRow = 10;
for (int i = 9; i >= 0; i--) {
for (int j = 9; j >= 0; j--) {
if (*dataPtr[i][j] == 0x50 && abs(closestPelletRow - 9) < abs(i - 9)) {
break;
breakTestRow = true;
}
if (breakTestRow) {
break;
}
}
}
bool breakTestCol = false;
// Check closest pellet column based on location of the fish
int closestPelletCol = 10;
for (int i = 9; i >= 0; i--) {
for (int j = 9; j >= 0; j--) {
if (*dataPtr[i][j] == 0x50 && abs(closestPelletCol - fishCurrentCol) > abs(j - fishCurrentCol)) {
break;
breakTestCol = true;
}
if (breakTestCol) {
break;
}
}
}
*dataPtr[9][fishCurrentCol] = '~';
if (distanceCol > 0) {
fishCurrentCol--;
}
else {
fishCurrentCol++;
}
*dataPtr[9][fishCurrentCol] = 'F';
if (distanceCol > 0) {
distanceCol--;
}
else {
distanceCol++;
}
struct timespec s;
s.tv_sec = 0;
s.tv_nsec = 500000000;
//nanosleep(&s, NULL);
sleep(1);
}
}
sleep(1);
}
exit(0);
}
void onInterrupt() {
fprintf(stderr, " (Fish) PID: %d - Died due to interrupt! ", getpid());
exit(0);
}
void onTermination() {
fprintf(stderr, " (Fish) PID: %d - Died due to termination! ", getpid());
exit(0);
}
Pellet.c
#define _XOPEN_SOURCE 700
#include <stdlib.h> // Standard library
#include <time.h> // For seeding rand
#include <unistd.h> // For fork
#include <signal.h> // For signal
#include <stdbool.h> // For bool
#include <stdio.h> // Standard I/O
#include <sys/types.h> // For types (such as pid_t)
#include <sys/ipc.h> // For interprocess communication
#include <sys/shm.h> // For shared memory
#include <sys/wait.h> // For wait
void onInterrupt();
void onTermination();
bool eaten = false;
int main(int argc, char *argv[]) {
signal(SIGINT, onInterrupt);
signal(SIGTERM, onTermination);
int sMemID; // Shared memory ID
key_t keyValue = 1337;
char (*dataPtr)[10][10]; // Pellet 2D Array
// Create shared memory segment. Exit on error.
if ((sMemID = shmget(keyValue, sizeof(char[10][10]), 0666)) < 0) {
perror("Could not create memory segment! ");
exit(1);
}
// Attach the shared memory segment. Exit on error.
if ((dataPtr = (char(*)[10][10])shmat(sMemID, NULL, 0)) == (char(*)[10][10]) -1) {
perror("Could not attach shared memory segment! ");
exit(1);
}
srand(time(NULL));
int row = rand() % 10;
int col = rand() % 10;
while (*dataPtr[row][col] != '~') {
row = rand() % 10;
col = rand() % 10;
}
*dataPtr[row][col] = 0x50; // My terminal doesn't support extended ASCII, so we won't be using 0x80.
while(row < 9) {
*dataPtr[row][col] = '~';
row++;
if (*dataPtr[row][col] == 'F') {
*dataPtr[row][col] |= 0x50;
eaten = true;
sleep(1);
break;
}
*dataPtr[row][col] = 0x50;
sleep(1);
}
if (!eaten) {
*dataPtr[row][col] = '~';
}
else if (*dataPtr[row][col] == 0x50) {
*dataPtr[row][col] = 'F';
}
sleep(1);
printf("Pellet PID: %d, X: %d, Y: %d - eaten: %s ", getpid(), row, col, eaten ? "true" : "false");
exit(0);
}
void onInterrupt() {
fprintf(stderr, " (Pellet) PID: %d - Died due to interrupt! - Eaten: %s ", getpid(), eaten ? "true" : "false");
exit(0);
}
void onTermination() {
fprintf(stderr, " (Fish) PID: %d - Died due to termination! - Eaten: %s ", getpid(), eaten ? "true" : "false");
exit(0);
}
Swim_Mill.c
#define _XOPEN_SOURCE 700
#include <stdlib.h> // Standard library
#include <time.h> // For seeding rand
#include <unistd.h> // For fork
#include <signal.h> // For signal
#include <stdbool.h> // For bool
#include <stdio.h> // Standard I/O
#include <sys/types.h> // For types (such as pid_t)
#include <sys/ipc.h> // For interprocess communication
#include <sys/shm.h> // For shared memory
#include <sys/wait.h> // For wait
void onInterrupt();
void onTermination();
bool eaten = false;
int main(int argc, char *argv[]) {
signal(SIGINT, onInterrupt);
signal(SIGTERM, onTermination);
int sMemID; // Shared memory ID
key_t keyValue = 1337;
char (*dataPtr)[10][10]; // Pellet 2D Array
// Create shared memory segment. Exit on error.
if ((sMemID = shmget(keyValue, sizeof(char[10][10]), 0666)) < 0) {
perror("Could not create memory segment! ");
exit(1);
}
// Attach the shared memory segment. Exit on error.
if ((dataPtr = (char(*)[10][10])shmat(sMemID, NULL, 0)) == (char(*)[10][10]) -1) {
perror("Could not attach shared memory segment! ");
exit(1);
}
srand(time(NULL));
int row = rand() % 10;
int col = rand() % 10;
while (*dataPtr[row][col] != '~') {
row = rand() % 10;
col = rand() % 10;
}
*dataPtr[row][col] = 0x50; // My terminal doesn't support extended ASCII, so we won't be using 0x80.
while(row < 9) {
*dataPtr[row][col] = '~';
row++;
if (*dataPtr[row][col] == 'F') {
*dataPtr[row][col] |= 0x50;
eaten = true;
sleep(1);
break;
}
*dataPtr[row][col] = 0x50;
sleep(1);
}
if (!eaten) {
*dataPtr[row][col] = '~';
}
else if (*dataPtr[row][col] == 0x50) {
*dataPtr[row][col] = 'F';
}
sleep(1);
printf("Pellet PID: %d, X: %d, Y: %d - eaten: %s ", getpid(), row, col, eaten ? "true" : "false");
exit(0);
}
void onInterrupt() {
fprintf(stderr, " (Pellet) PID: %d - Died due to interrupt! - Eaten: %s ", getpid(), eaten ? "true" : "false");
exit(0);
}
void onTermination() {
fprintf(stderr, " (Fish) PID: %d - Died due to termination! - Eaten: %s ", getpid(), eaten ? "true" : "false");
exit(0);
}
makefile
all: Fish Pellet Swim_Mill
Fish: Fish.o
gcc -o Fish Fish.o
Fish.o: Fish.c
gcc -c Fish.c -o Fish.o -std=c99
Pellet: Pellet.o
gcc -o Pellet Pellet.o
Pellet.o: Pellet.c
gcc -c Pellet.c -o Pellet.o -std=c99
Swim_Mill: Swim_Mill.o
gcc -o Swim_Mill Swim_Mill.o
Swim_Mill.o: Swim_Mill.c
gcc -c Swim_Mill.c -o Swim_Mill.o -std=c99
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.